2010-06-28 81 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