JAXB Marshalling Unmarshalling с CDATA
Я пытаюсь сделать маршалинг с JAXB.
мой вывод похож на
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<name><![CDATA[<h1>kshitij</h1>]]></name>
<surname><h1>solanki</h1></surname>
<id><h1>1</h1></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;
}
}
Ответы
Ответ 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("<", "<").replaceAll(">", ">");
}
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><p>Jorge & Mary</p></name>
</root>
*/