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>
これを行うには、次のコードを使用しています。コードのコメントを外すと、プロパティバインド例外が発生します。それなしではコンパイルできますが、必要な出力を正確に取得できません。
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;
}
}
次のことができます。
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;
}
}
ルート
@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
でラップして、目的の効果を得る必要がありました。また、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/Output
<?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>
注意してください:私は EclipseLink JAXB(MOXy) リードおよび JAXB(JSR-222) エキスパートグループのメンバー。
JAXB(JSR-222)プロバイダーとしてMOXyを使用する場合、ユースケースに@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
JAXBプロバイダーとしてMOXyを使用するには、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);
}
}
input.xml/Output
以下は、デモコードの実行に対する入力と出力です。
<?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>
詳細情報
この質問を掘り下げて、新しい回答を投稿してすみません(私の担当者はまだコメントするには十分ではありません...)。私は同じ問題にぶつかり、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
を実装しました。
public class CDataAwareUtfEncodedXmlCharacterEscapeHandlerは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を呼び出すこともできます。
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);
}
}
@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>
*/
同様の問題の解決策を見つけようとしてこのページに行きましたが、これを解決する別のアプローチを見つけました。この問題を解決する1つの方法は、XMLをSAX2イベントとしてハンドラーに送信し、ハンドラーにロジックを記述してCDATAタグをXMLに追加することです。このアプローチでは、注釈を追加する必要はありません。マーシャリングされるクラスがXSDから生成されるシナリオで役立ちます。
マーシャリングされるXSDから生成されたクラスにStringフィールドがあり、StringフィールドにCDATAタグ内に配置される特殊文字が含まれているとします。
@XmlRootElement
public class TestingCDATA{
public String xmlContent;
}
まず、コンテンツハンドラーでメソッドをオーバーライドできる適切なクラスを検索します。そのようなクラスの1つは、パッケージcom.Sun.xml.txw2.outputにあるXMLWriterです。jdk1.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>