30

我正在尝试使用 JAXB 进行编组。

我的输出是这样的:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <name>&lt;![CDATA[&lt;h1&gt;kshitij&lt;/h1&gt;]]&gt;</name>
    <surname>&lt;h1&gt;solanki&lt;/h1&gt;</surname>
    <id>&lt;h1&gt;1&lt;/h1&gt;</id>
</root>

...但我需要这样的输出:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <root>
        <name><![CDATA[<h1>kshitij</h1>]]></name>
        <surname><![CDATA[<h1>solanki</h1>]]></surname>
        <id><![CDATA[0]]></id>
    </root>

我正在使用以下代码来执行此操作。

如果我取消注释代码,我会得到PropertyBindingException. 没有它我可以编译,但我没有得到所需的确切输出。

  package com.ksh.templates;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import com.sun.xml.bind.marshaller.CharacterEscapeHandler;

public class MainCDATA {
    public static void main(String args[])
    {
        try
        {
            String name = "<h1>kshitij</h1>";
            String surname = "<h1>solanki</h1>";
            String id = "<h1>1</h1>";
            
            TestingCDATA cdata = new TestingCDATA();
            cdata.setId(id);
            cdata.setName(name);
            cdata.setSurname(surname);
            
            JAXBContext jaxbContext = JAXBContext.newInstance(TestingCDATA.class);
            Marshaller marshaller = jaxbContext.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            
            marshaller.setProperty(CharacterEscapeHandler.class.getName(), new CharacterEscapeHandler() { 
                public void escape(char[] ac, int i, int j, boolean flag,
                Writer writer) throws IOException {
                writer.write( ac, i, j ); }
                });
            StringWriter stringWriter = new StringWriter(); 
            marshaller.marshal(cdata, stringWriter);
            System.out.println(stringWriter.toString());
        } catch (Exception e) {
            System.out.println(e);
        }       
    }
}

我的豆子看起来像这样:

package com.ksh.templates;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import com.sun.xml.txw2.annotation.XmlCDATA;

@XmlRootElement(name = "root")
@XmlAccessorType(XmlAccessType.FIELD)
public class TestingCDATA {

    @XmlElement
    @XmlJavaTypeAdapter(value = AdaptorCDATA.class)
    private String name;
    @XmlElement
    @XmlJavaTypeAdapter(value = AdaptorCDATA.class)
    private String surname;
    
    @XmlCDATA
    public String getName() {
        return name;
    }
    @XmlCDATA
    public void setName(String name) {
        this.name = name;
    }
    @XmlCDATA
    public String getSurname() {
        return surname;
    }
    @XmlCDATA
    public void setSurname(String surname) {
        this.surname = surname;
    }
}

适配器类

public class AdaptorCDATA extends XmlAdapter<String, String> {

    @Override
    public String marshal(String arg0) throws Exception {
        return "<![CDATA[" + arg0 + "]]>";
    }
    @Override
    public String unmarshal(String arg0) throws Exception {
        return arg0;
    }
}
4

7 回答 7

44

您可以执行以下操作:

适配器CDATA

package forum14193944;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class AdapterCDATA extends XmlAdapter<String, String> {

    @Override
    public String marshal(String arg0) throws Exception {
        return "<![CDATA[" + arg0 + "]]>";
    }
    @Override
    public String unmarshal(String arg0) throws Exception {
        return arg0;
    }

}

@XmlJavaTypeAdapter注释用于指定XmlAdapter应该使用的。

package forum14193944;

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {

    @XmlJavaTypeAdapter(AdapterCDATA.class)
    private String name;

    @XmlJavaTypeAdapter(AdapterCDATA.class)
    private String surname;

    @XmlJavaTypeAdapter(AdapterCDATA.class)
    private String id;

}

演示

我必须包裹起来System.out才能OutputStreamWriter获得预期的效果。另请注意,设置 aCharacterEscapeHandler意味着它负责该 的所有转义处理Marshaller

package forum14193944;

import java.io.*;
import javax.xml.bind.*;
import com.sun.xml.bind.marshaller.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum14193944/input.xml");
        Root root = (Root) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(CharacterEscapeHandler.class.getName(),
                new CharacterEscapeHandler() {
                    @Override
                    public void escape(char[] ac, int i, int j, boolean flag,
                            Writer writer) throws IOException {
                        writer.write(ac, i, j);
                    }
                });
        marshaller.marshal(root, new OutputStreamWriter(System.out));
    }

}

输入.xml/输出

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <name><![CDATA[<h1>kshitij</h1>]]></name>
    <surname><![CDATA[<h1>solanki</h1>]]></surname>
    <id><![CDATA[0]]></id>
</root>
于 2013-01-07T14:25:01.567 回答
14

请注意: 我是EclipseLink JAXB (MOXy)负责人,也是JAXB (JSR-222)专家组的成员。

如果您使用 MOXy 作为您的 JAXB (JSR-222) 提供程序,那么您可以将@XmlCDATA扩展用于您的用例。

@XmlCDATA注释用于指示您希望将字段/属性的内容包装在 CDATA 部分中。@XmlCDATA注释可以与 结合使用@XmlElement

package forum14193944;

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

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {

    @XmlCDATA
    private String name;

    @XmlCDATA
    private String surname;

    @XmlCDATA
    private String id;

}

jaxb.properties

要将 MOXy 用作您的 JAXB 提供程序,您需要添加以jaxb.properties以下条目命名的文件。

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

演示

下面是一些演示代码,以证明一切正常。

package forum14193944;

import java.io.File;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum14193944/input.xml");
        Root root = (Root) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);
    }

}

输入.xml/输出

下面是运行演示代码的输入和输出。

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <name><![CDATA[<h1>kshitij</h1>]]></name>
   <surname><![CDATA[<h1>solanki</h1>]]></surname>
   <id><![CDATA[0]]></id>
</root>

了解更多信息

于 2013-01-07T11:07:12.400 回答
7

很抱歉挖掘了这个问题,并发布了一个新的答案(我的代表还不够高,无法发表评论......)。我遇到了同样的问题,我尝试了 Blaise Doughan 的答案,但从我的测试来看,要么它不涵盖所有情况,要么我在某处做错了什么。



    marshaller.setProperty(CharacterEscapeHandler.class.getName(),
                    new CharacterEscapeHandler() {
                        @Override
                        public void escape(char[] ac, int i, int j, boolean flag,
                                Writer writer) throws IOException {
                            writer.write(ac, i, j);
                        }
                    });


从我的测试中,无论您是否@XmlJavaTypeAdapter(AdapterCDATA.class)在属性上使用注释,此代码都会删除所有转义...

为了解决这个问题,我实现了以下内容CharacterEscapeHandler

    公共类 CDataAwareUtfEncodedXmlCharacterEscapeHandler 实现 CharacterEscapeHandler {

        private static final char[] cDataPrefix = "<![CDATA[".toCharArray();
        private static final char[] cDataSuffix = "]]>".toCharArray();

        公共静态最终 CDataAwareUtfEncodedXmlCharacterEscapeHandler 实例 = new CDataAwareUtfEncodedXmlCharacterEscapeHandler();

        私人 CDataAwareUtfEncodedXmlCharacterEscapeHandler() {
        }

        @覆盖
        public void escape(char[] ch, int start, int length, boolean isAttVal, Writer out) 抛出 IOException {
            布尔 isCData = 长度 > cDataPrefix.length + cDataSuffix.length;
            如果(isCData){
                for (int i = 0, j = start; i < cDataPrefix.length; ++i, ++j) {
                    if (cDataPrefix[i] != ch[j]) {
                        isCData = 假;
                        休息;
                    }
                }
                如果(isCData){
                    for (int i = cDataSuffix.length - 1, j = start + length - 1; i >= 0; --i, --j) {
                        if (cDataSuffix[i] != ch[j]) {
                            isCData = 假;
                            休息;
                        }
                    }
                }
            }
            如果(isCData){
                out.write(ch, start, length);
            } 别的 {
                MinimumEscapeHandler.theInstance.escape(ch, start, length, isAttVal, out);
            }
        }
    }

如果您的编码不是 UTF*,您可能不想调用 MinimumEscapeHandler,而是调用 NioEscapeHandler 甚至 DumbEscapeHandler。

于 2016-12-28T01:18:09.290 回答
0
    @Test
    public void t() throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        Root root = new Root();
        root.name = "<p>Jorge & Mary</p>";
        marshaller.marshal(root, System.out);
    }
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class Root {
        @XmlCDATA
        private String name;
    }
    /* WHAT I SEE IN THE CONSOLE
     * 
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <name>&lt;p&gt;Jorge &amp; Mary&lt;/p&gt;</name>
</root>
     */
于 2014-04-15T19:49:26.877 回答
0

com.sun.internal 不适用于 play2,但可以

private static String marshal(YOurCLass xml){
    try{
        StringWriter stringWritter = new StringWriter();
        Marshaller marshaller = JAXBContext.newInstance(YourCLass.class).createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(Marshaller.JAXB_ENCODING, "ISO-8859-1");
        marshaller.marshal(xml, stringWritter);
        return stringWritter.toString().replaceAll("&lt;", "<").replaceAll("&gt;", ">");
    }
    catch(JAXBException e){
        throw new RuntimeException(e);
    }
}
于 2014-12-01T17:54:15.910 回答
0

我登陆此页面试图找到类似问题的解决方案,我找到了另一种解决此问题的方法。解决此问题的一种方法是将 XML 作为 SAX2 事件发送到处理程序,然后在处理程序中编写逻辑以将 CDATA 标记添加到 XML。这种方法不需要添加任何注释。在从 XSD 生成要编组的类的情况下很有用。

假设您在从 XSD 生成的类中有一个要编组的字符串字段,并且该字符串字段包含要放入 CDATA 标记内的特殊字符。

@XmlRootElement
public class TestingCDATA{
    public String xmlContent;

}

我们将从搜索一个合适的类开始,它的方法可以在我们的内容处理程序中被覆盖。一个这样的类是 com.sun.xml.txw2.output 包中的 XMLWriter,它在 jdk 1.7 和 1.8 中可用

import com.sun.xml.txw2.output.XMLWriter;
import org.xml.sax.SAXException;

import java.io.IOException;
import java.io.Writer;
import java.util.regex.Pattern;

public class CDATAContentHandler extends 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();
        }
    }
}

我们正在重写 characters 方法,使用正则表达式来检查是否包含任何特殊字符。如果找到它们,那么我们在它们周围放置 CDATA 标记。在这种情况下,XMLWriter 负责添加 CDATA 标记。

我们将使用以下代码进行编组:

public String addCDATAToXML(TestingCDATA request) throws FormatException {
    try {
        JAXBContext jaxbContext = JAXBContext.newInstance(TestingCDATA.class);
        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
        StringWriter sw = new StringWriter();
        CDATAContentHandler cDataContentHandler = new CDATAContentHandler(sw, "UTF-8");
        jaxbMarshaller.marshal(request, cDataContentHandler);
        return sw.toString();
    } catch (JAXBException | IOException e) {
        throw new FormatException("Unable to add CDATA for request", e);
    }
}

如果我们按如下所述传递要编组的请求,这将编组对象并返回 XML。

TestingCDATA request=new TestingCDATA();
request.xmlContent="<?xml>";

System.out.println(addCDATAToXML(request)); // Would return the following String

Output- 

<?xml version="1.0" encoding="UTF-8"?>
<testingCDATA>
<xmlContent><![CDATA[<?xml>]]></xmlContent>
</testingCDATA>
于 2017-02-28T09:52:35.913 回答
0

除了@bdoughan 的回答。支持 CDATA 的字符转义处理程序

import com.sun.xml.bind.marshaller.CharacterEscapeHandler;

import java.io.IOException;
import java.io.Writer;

/**
 * This class is a modern version of JAXB MinimumEscapeHandler with CDATA support
 *
 * @author me
 * @see com.sun.xml.bind.marshaller.MinimumEscapeHandler
 */
public class CdataEscapeHandler implements CharacterEscapeHandler {

    private CdataEscapeHandler() {
    }  // no instanciation please

    public static final CharacterEscapeHandler theInstance = new CdataEscapeHandler();

    @Override
    public void escape(char[] ch, int start, int length, boolean isAttVal, Writer out) throws IOException {
        // avoid calling the Writerwrite method too much by assuming
        // that the escaping occurs rarely.
        // profiling revealed that this is faster than the naive code.
        int limit = start + length;
        for (int i = start; i < limit; i++) {
            if (!isAttVal
                    && i <= limit - 12
                    && ch[i] == '<'
                    && ch[i + 1] == '!'
                    && ch[i + 2] == '['
                    && ch[i + 3] == 'C'
                    && ch[i + 4] == 'D'
                    && ch[i + 5] == 'A'
                    && ch[i + 6] == 'T'
                    && ch[i + 7] == 'A'
                    && ch[i + 8] == '[') {
                int cdataEnd = i + 8;
                for (int k = i + 9; k < limit - 2; k++) {
                    if (ch[k] == ']'
                            && ch[k + 1] == ']'
                            && ch[k + 2] == '>') {
                        cdataEnd = k + 2;
                        break;
                    }
                }
                out.write(ch, start, cdataEnd + 1);
                if (cdataEnd == limit - 1) return;
                start = i = cdataEnd + 1;
            }
            char c = ch[i];
            if (c == '&' || c == '<' || c == '>' || c == '\r' || (c == '\"' && isAttVal)) {
                if (i != start)
                    out.write(ch, start, i - start);
                start = i + 1;
                switch (ch[i]) {
                    case '&':
                        out.write("&amp;");
                        break;
                    case '<':
                        out.write("&lt;");
                        break;
                    case '>':
                        out.write("&gt;");
                        break;
                    case '\"':
                        out.write("&quot;");
                        break;
                }
            }
        }

        if (start != limit)
            out.write(ch, start, limit - start);
    }
}

于 2020-01-27T14:41:16.217 回答