web-dev-qa-db-ja.com

JAXBマーシャリング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>

これを行うには、次のコードを使用しています。コードのコメントを外すと、プロパティバインド例外が発生します。それなしではコンパイルできますが、必要な出力を正確に取得できません。

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

次のことができます。

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.outOutputStreamWriterでラップして、目的の効果を得る必要がありました。また、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>
37
bdoughan

注意してください:私は 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>

詳細情報

12
bdoughan

この質問を掘り下げて、新しい回答を投稿してすみません(私の担当者はまだコメントするには十分ではありません...)。私は同じ問題にぶつかり、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を呼び出すこともできます。

3
Marc P.

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);
    }
}
1
Bruno
    @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>
     */
0
user1346730

同様の問題の解決策を見つけようとしてこのページに行きましたが、これを解決する別のアプローチを見つけました。この問題を解決する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>
0
pkoli