2010-06-28 79 views
38

我正在使用JAXB將我的數據序列化爲XML。類代碼很簡單,如下所示。我想生成包含CDATA塊的XML以獲取某些Args的值。例如,當前的代碼生成此XML:如何使用JAXB生成CDATA塊?

<command> 
    <args> 
     <arg name="test_id">1234</arg> 
     <arg name="source">&lt;html>EMAIL&lt;/html></arg> 
    </args> 
</command> 

我想換行「源」 ARG在CDATA,使得它看起來像如下:

<command> 
    <args> 
     <arg name="test_id">1234</arg> 
     <arg name="source"><[![CDATA[<html>EMAIL</html>]]></arg> 
    </args> 
</command> 

我怎樣才能在下面的代碼實現這一點?

@XmlRootElement(name="command") 
public class Command { 

     @XmlElementWrapper(name="args") 
     protected List<Arg> arg; 
    } 
@XmlRootElement(name="arg") 
public class Arg { 

     @XmlAttribute 
     public String name; 
     @XmlValue 
     public String value; 

     public Arg() {}; 

     static Arg make(final String name, final String value) { 
      Arg a = new Arg(); 
      a.name=name; a.value=value; 
      return a; } 
    } 
+0

你能找到任何解決這一問題?如果是,請分享,謝謝。 – Javatar 2012-02-15 09:24:14

回答

24

注:我是EclipseLink JAXB (MOXy)鉛和JAXB (JSR-222)專家小組的成員。

如果您正在使用莫西爲您的JAXB提供者,那麼你可以利用@XmlCDATA擴展:

package blog.cdata; 

import javax.xml.bind.annotation.XmlRootElement; 
import org.eclipse.persistence.oxm.annotations.XmlCDATA; 

@XmlRootElement(name="c") 
public class Customer { 

    private String bio; 

    @XmlCDATA 
    public void setBio(String bio) { 
     this.bio = bio; 
    } 

    public String getBio() { 
     return bio; 
    } 

} 

更多信息

+3

我不確定爲什麼這個回覆收到了反對票。這是對問題的直接回應,並提供了有關如何應用解決方案的詳細說明的鏈接。 JAXB是一個規範,兼容的實現例如MOXy包含擴展來處理諸如CDATA之類的事情。 – 2010-07-15 13:09:12

+0

我可以在您的博客上看到解決方案,我希望找到不需要使用任何第三方Jar的東西。但是我發現它不受Sun JDK附帶的JAXB實現的支持。 – Shreerang 2010-07-22 18:44:43

+4

當我試圖解決這個問題時,這個鏈接一直在彈出。我相信它工作得很好,但令我感到困惑的是,我無法理解如何將解決方案插入到客戶端。每個例子都使用''main''方法來證明編組能夠起作用,但是它們缺少一個如何在真實客戶端中使用它的部分。例如,應該在wsdl2java生成的客戶端中「JAXBContext jc = JAXBContext.newInstance(classes,props);'應該去哪裏,因爲這個JAXBContext是由jax-ws自動調用的,如果我理解正確的話。 – 2012-10-07 15:09:53

10

這裏是用上述的站點引用的代碼示例:

import java.io.File; 
import java.io.StringWriter; 

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Marshaller; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.parsers.DocumentBuilder; 
import javax.xml.parsers.DocumentBuilderFactory; 

import org.apache.xml.serialize.OutputFormat; 
import org.apache.xml.serialize.XMLSerializer; 
import org.w3c.dom.Document; 

public class JaxbCDATASample { 

    public static void main(String[] args) throws Exception { 
     // unmarshal a doc 
     JAXBContext jc = JAXBContext.newInstance("..."); 
     Unmarshaller u = jc.createUnmarshaller(); 
     Object o = u.unmarshal(...); 

     // create a JAXB marshaller 
     Marshaller m = jc.createMarshaller(); 

     // get an Apache XMLSerializer configured to generate CDATA 
     XMLSerializer serializer = getXMLSerializer(); 

     // marshal using the Apache XMLSerializer 
     m.marshal(o, serializer.asContentHandler()); 
    } 

    private static XMLSerializer getXMLSerializer() { 
     // configure an OutputFormat to handle CDATA 
     OutputFormat of = new OutputFormat(); 

     // specify which of your elements you want to be handled as CDATA. 
     // The use of the '^' between the namespaceURI and the localname 
     // seems to be an implementation detail of the xerces code. 
     // When processing xml that doesn't use namespaces, simply omit the 
     // namespace prefix as shown in the third CDataElement below. 
     of.setCDataElements(
      new String[] { "ns1^foo", // <ns1:foo> 
        "ns2^bar", // <ns2:bar> 
        "^baz" }); // <baz> 

     // set any other options you'd like 
     of.setPreserveSpace(true); 
     of.setIndenting(true); 

     // create the serializer 
     XMLSerializer serializer = new XMLSerializer(of); 
     serializer.setOutputByteStream(System.out); 

     return serializer; 
    } 
} 
2

作爲澤爾士-J 2.9,XMLSerializer的已被棄用。建議將其替換爲DOM Level 3 LSSerializer或JAXP的XML Transformation API。有沒有人嘗試過方法?

+0

我正在嘗試做,我發現了一個鏈接:http://www.mirthcorp.com/community/fisheye/rdiff/Mirth/trunk/server/src/com/webreach/mirth/model/converters/DocumentSerializer.java ?r1 = 1809&r2 = 1881&u&N – 2012-02-17 09:39:25

18

使用JAXB的Marshaller#marshal(ContentHandler)編組爲ContentHandler對象。只需覆蓋您使用了ContentHandler實現(如JDOM的SAXHandler,Apache的XMLSerializer,等等)characters方法:

public class CDataContentHandler extends (SAXHandler|XMLSerializer|Other...) { 
    // see http://www.w3.org/TR/xml/#syntax 
    private static final Pattern XML_CHARS = Pattern.compile("[<>&]"); 

    public void characters(char[] ch, int start, int length) throws SAXException { 
     boolean useCData = XML_CHARS.matcher(new String(ch,start,length)).find(); 
     if (useCData) super.startCDATA(); 
     super.characters(ch, start, length); 
     if (useCData) super.endCDATA(); 
    } 
} 

這是比使用XMLSerializer.setCDataElements(...)方法要好得多,因爲你不必硬編碼任何元素列表。僅當需要一個時,它會自動輸出CDATA塊

+0

乾淨簡單。我打算通過一些測試。謝謝! – ericp 2012-01-25 17:10:26

+0

我無法擴展DataWriter類並使用此過程嗎?我是默認的contentHandler,所以我無法擴展它並使用它來解決我的問題。 – 2013-03-19 09:40:31

+0

@Apoorvasahay最後,我在JDK 8中找到了類中的一個類,即'com.sun.xml.internal.txw2.output.XMLWriter'。有關詳細信息,請參閱我的主頁。 – bluearrow 2016-09-18 12:31:34

5

下面的簡單方法將CDATA支持在JAX-B,它不支持CDATA天然:

  1. 聲明一個定製簡單類型CDataString延伸串,以確定應通過CDATA
  2. 可以處理的字段
  3. 在CDataString創建定製CDataAdapter一個分析和打印內容
  4. 使用JAXB綁定鏈接CDataString和你CDataAdapter。該CdataAdapter將添加/馬歇爾/解組時間CdataStrings消除對/
  5. 聲明一個自定義字符轉義處理不轉義字符打印CDATA字符串時,並將此作爲的Marshaller CharacterEscapeEncoder

的Et瞧,任何CDataString元素都將在Marshall時間封裝。在unmarshall時間,會自動刪除。

+1

請參閱這裏的代碼示例:http://stackoverflow.com/a/14197860/809536 – ZiglioUK 2015-12-02 01:41:36

14

解決方案審查:

  • 弗雷德的答案僅僅是一個解決辦法,同時驗證內容時現Marshaller鏈接到架構,因爲你只修改字符串常量,不產生CDATA節將失敗。所以,如果你只從重寫字符串<![CDATA [富]]>字符串的長度由使用的Xerces 15而不是3
  • 的莫西解決方案的認可是實現特定的,並且不僅與JDK的類一起工作。
  • getSerializer的解決方案引用了棄用的XMLSerializer類。
  • 解決方案LSSerializer只是一個痛苦。

我通過使用XMLStreamWriter實現修改了a2ndrade的解決方案。該解決方案效果很好。

XMLOutputFactory xof = XMLOutputFactory.newInstance(); 
XMLStreamWriter streamWriter = xof.createXMLStreamWriter(System.out); 
CDataXMLStreamWriter cdataStreamWriter = new CDataXMLStreamWriter(streamWriter); 
marshaller.marshal(jaxbElement, cdataStreamWriter); 
cdataStreamWriter.flush(); 
cdataStreamWriter.close(); 

這就是CDataXMLStreamWriter實現。委託類只是將所有方法調用委託給給定的XMLStreamWriter實現。

import java.util.regex.Pattern; 
import javax.xml.stream.XMLStreamException; 
import javax.xml.stream.XMLStreamWriter; 

/** 
* Implementation which is able to decide to use a CDATA section for a string. 
*/ 
public class CDataXMLStreamWriter extends DelegatingXMLStreamWriter 
{ 
    private static final Pattern XML_CHARS = Pattern.compile("[&<>]"); 

    public CDataXMLStreamWriter(XMLStreamWriter del) 
    { 
     super(del); 
    } 

    @Override 
    public void writeCharacters(String text) throws XMLStreamException 
    { 
     boolean useCData = XML_CHARS.matcher(text).find(); 
     if(useCData) 
     { 
     super.writeCData(text); 
     } 
     else 
     { 
     super.writeCharacters(text); 
     } 
    } 
} 
+2

DelegatingXMLStreamWriter:https://github.com/apache/cxf/blob/master/core/src/main/java/org/ apache/cxf/staxutils/DelegatingXMLStreamWriter.java – 2017-04-03 15:37:39

8

出於與Michael Ernst相同的原因,我對這裏的大部分答案都不滿意。我無法使用他的解決方案,因爲我的要求是將CDATA標記放入一組定義的字段中 - 就像在raiglstorfer的OutputFormat解決方案中一樣。

我的解決方案是編組到DOM文檔,然後做空XSL轉換做輸出。變形金剛允許您設置哪些元素包裹在CDATA標籤中。

Document document = ... 
jaxbMarshaller.marshal(jaxbObject, document); 

Transformer nullTransformer = TransformerFactory.newInstance().newTransformer(); 
nullTransformer.setOutputProperty(OutputKeys.INDENT, "yes"); 
nullTransformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, "myElement {myNamespace}myOtherElement"); 
nullTransformer.transform(new DOMSource(document), new StreamResult(writer/stream)); 

更多的信息在這裏:http://javacoalface.blogspot.co.uk/2012/09/outputting-cdata-sections-with-jaxb.html

+0

這很好用。這是一個簡單的解決方案,但CDATA元素必須在元帥時間定義。 – migu 2013-11-12 16:59:51

0

下面的代碼將會阻止編碼CDATA元素:

Marshaller marshaller = context.createMarshaller(); 
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); 
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 

StringWriter stringWriter = new StringWriter(); 
PrintWriter printWriter = new PrintWriter(stringWriter); 
DataWriter dataWriter = new DataWriter(printWriter, "UTF-8", new CharacterEscapeHandler() { 
    @Override 
    public void escape(char[] buf, int start, int len, boolean b, Writer out) throws IOException { 
     out.write(buf, start, len); 
    } 
}); 

marshaller.marshal(data, dataWriter); 

System.out.println(stringWriter.toString()); 

它也將保持UTF-8爲您的編碼。

+0

然後,當其他一些非CDATA字段出現時,使用<>,然後將其擰緊。夥計們,這是非常糟糕的做法。我在這裏看過幾次,但我不會推薦它。逃避方法在那裏,因爲你逃避了一些事情,並不是因爲你根本不想逃避。 – Mejmo 2017-07-24 06:48:49

0

只是一個警告:根據javax.xml.transform.Transformer.setOutputProperty(...)的文檔,當從另一個命名空間指示一個元素時,應該使用限定名稱的語法。據的JavaDoc(爪哇1.6 rt.jar中):

「(...)例如,如果從與所限定的元件得到一個URI和本地名稱,那麼合格的名稱將是」 {} http://xyz.foo.com/yada/baz.html FOO。請注意,不使用前綴。「

嗯,這不工作 - 從Java 1.6的rt.jar實現類,這意味着com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl解釋屬於不同的命名空間的元素只那麼正確,當它們被聲明爲「http://xyz.foo.com/yada/baz.html:foo」,因爲在執行有人解析它尋找最後一個冒號因此而不調:

transformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, "{http://xyz.foo.com/yada/baz.html}foo") 

應根據JavaDoc的工作,但最終被解析作爲「http」和「//xyz.foo.com/yada/baz.html」,您必須調用

transformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, "http://xyz.foo.com/yada/baz.html:foo") 

至少在Java 1.6中。

4

補充@a2ndrade的答案。

我發現一個類可以在JDK 8中進行擴展。但注意到類是在com.sun包中。您可以製作一份代碼,以防在未來的JDK中刪除此類。

public class CDataContentHandler extends com.sun.xml.internal.txw2.output.XMLWriter { 
    public CDataContentHandler(Writer writer, String encoding) throws IOException { 
    super(writer, encoding); 
    } 

    // see http://www.w3.org/TR/xml/#syntax 
    private static final Pattern XML_CHARS = Pattern.compile("[<>&]"); 

    public void characters(char[] ch, int start, int length) throws SAXException { 
    boolean useCData = XML_CHARS.matcher(new String(ch, start, length)).find(); 
    if (useCData) { 
     super.startCDATA(); 
    } 
    super.characters(ch, start, length); 
    if (useCData) { 
     super.endCDATA(); 
    } 
    } 
} 

如何使用:

JAXBContext jaxbContext = JAXBContext.newInstance(...class); 
    Marshaller marshaller = jaxbContext.createMarshaller(); 
    StringWriter sw = new StringWriter(); 
    CDataContentHandler cdataHandler = new CDataContentHandler(sw,"utf-8"); 
    marshaller.marshal(gu, cdataHandler); 
    System.out.println(sw.toString()); 

結果例如:

<?xml version="1.0" encoding="utf-8"?> 
<genericUser> 
    <password><![CDATA[dskfj>><<]]></password> 
    <username>UNKNOWN::UNKNOWN</username> 
    <properties> 
    <prop2>v2</prop2> 
    <prop1><![CDATA[v1><]]></prop1> 
    </properties> 
    <timestamp/> 
    <uuid>cb8cbc487ee542ec83e934e7702b9d26</uuid> 
</genericUser> 
+0

感謝您的回答@bluearrow。我遵循了這些步驟,但得到了關於com.sun.xml.internal.txw2.output.XMLWriter的錯誤,我可以使用http://stackoverflow.com/a/33917172/3161688解決這個錯誤。謝謝! – 2016-10-07 04:15:06