Подтвердить что ты не робот

JAXB Marshalling Unmarshalling с CDATA

Я пытаюсь сделать маршалинг с 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>

Я использую следующий код для этого. и если я раскомментирую код, я получаю Property Binding Exception. Без этого я могу скомпилировать, но я не получаю точный требуемый вывод.

  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);
        }       
    }
}

и мой bean lokks нравится

 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;
    }
}
4b9b3361

Ответ 1

Вы можете сделать следующее:

AdapterCDATA

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;
    }

}

Root

Аннотация @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;

}

Demo

Мне пришлось обернуть System.out в OutputStreamWriter, чтобы получить желаемый эффект. Также обратите внимание, что установка CharacterEscapeHandler означает, что он отвечает за все управляющие операции для этого 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));
    }

}

Input.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>

Ответ 2

Обратите внимание: Я EclipseLink JAXB (MOXy) и членом группы экспертов JAXB (JSR-222).

Если вы используете MOXy в качестве поставщика JAXB (JSR-222), вы можете использовать расширение @XmlCDATA для вашего использования.

Root

Аннотация @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

Demo

Ниже приведен какой-то демонстрационный код, подтверждающий, что все работает.

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);
    }

}

Input.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>

Дополнительная информация

Ответ 3

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);
    }
}

Ответ 4

Извините за то, что выкалываете этот вопрос и публикуете новый ответ (мой представитель еще недостаточно высок, чтобы комментировать...). Я столкнулся с тем же вопросом, я попробовал ответ Блейз Дохан, но из моих тестов либо он не охватывает все случаи, либо я что-то делаю неправильно.



    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:

    public class CDataAwareUtfEncodedXmlCharacterEscapeHandler implements CharacterEscapeHandler {

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

        public static final CDataAwareUtfEncodedXmlCharacterEscapeHandler instance = new CDataAwareUtfEncodedXmlCharacterEscapeHandler();

        private CDataAwareUtfEncodedXmlCharacterEscapeHandler() {
        }

        @Override
        public void escape(char[] ch, int start, int length, boolean isAttVal, Writer out) throws IOException {
            boolean isCData = length > cDataPrefix.length + cDataSuffix.length;
            if (isCData) {
                for (int i = 0, j = start; i < cDataPrefix.length; ++i, ++j) {
                    if (cDataPrefix[i] != ch[j]) {
                        isCData = false;
                        break;
                    }
                }
                if (isCData) {
                    for (int i = cDataSuffix.length - 1, j = start + length - 1; i >= 0; --i, --j) {
                        if (cDataSuffix[i] != ch[j]) {
                            isCData = false;
                            break;
                        }
                    }
                }
            }
            if (isCData) {
                out.write(ch, start, length);
            } else {
                MinimumEscapeHandler.theInstance.escape(ch, start, length, isAttVal, out);
            }
        }
    }

Если ваша кодировка не UTF *, вы можете не захотеть вызвать MinimumEscapeHandler, а скорее NioEscapeHandler или даже DumbEscapeHandler.

Ответ 5

Я приземлился на этой странице, пытаясь найти решение аналогичной проблемы, я нашел другой подход для решения этой проблемы. Один из способов решения этой проблемы - отправить XML как события SAX2 в обработчик, а затем написать логику в обработчике, чтобы добавить теги CDATA в XML. Этот подход не требует добавления аннотации. Полезно в сценариях, где классы, подлежащие маршалированию, генерируются из XSD.

Предположим, у вас есть поле String в классе, сгенерированном из XSD, который должен быть маршалирован, а в поле String содержатся специальные символы, которые должны быть помещены в тег CDATA.

@XmlRootElement
public class TestingCDATA{
    public String xmlContent;

}

Мы начнем с поиска подходящего класса, метод которого может быть переопределен в нашем обработчике содержимого. Одним из таких классов является XMLWriter, найденный в пакете com.sun.xml.txw2.output, он доступен в 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();
        }
    }
}

Мы переопределяем метод символов, используя регулярное выражение, чтобы проверить, содержатся ли какие-либо специальные символы. Если они обнаружены, мы помещаем теги 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>

Ответ 6

    @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>
     */