4

この例を考えてみましょう -

タイプ Message のフィールドを持つ Report というクラスがあります。Message クラスには、文字列である「body」というフィールドがあります。「本文」には任意の文字列を指定できますが、適切にフォーマットされた XML コンテンツが含まれている場合があります。「本文」に XML コンテンツが含まれている場合、シリアライゼーションが現在提供されているものではなく、XML 構造の形式をとるようにするにはどうすればよいですか?

出力付きのコードは次のとおりです-

レポートクラス

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement(name = "Report")
@XmlType(propOrder = { "message"})
public class Report
{
    private Message message;
    public Message getMessage() { return message; }
    public void setMessage(Message m) { message = m; }
}

メッセージクラス

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;

@XmlType(propOrder = { "body" })
public class Message
{
    private String body;
    public String getBody() { return body; }
    @XmlElement
    public void setBody(String body) { this.body = body; }
}

主要

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;



public class SerializationTest
{
    public static void main(String args[]) throws Exception
    {
       JAXBContext jaxbContext = JAXBContext.newInstance(Report.class);
       Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
       jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

       Report report = new Report();
       Message message = new Message();

       message.setBody("Sample report message.");
       report.setMessage(message);
       jaxbMarshaller.marshal(report, System.out);

       message.setBody("<rootTag><body>All systems online.</body></rootTag>");
       report.setMessage(message);
       jaxbMarshaller.marshal(report, System.out);
    }
}

出力は次のとおりです-

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Report>
    <message>
        <body>Sample report message.</body>
    </message>
</Report>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Report>
    <message>
        <body>&lt;rootTag&gt;&lt;body&gt;All systems online.&lt;/body&gt;&lt;/rootTag&gt;</body>
    </message>
</Report>

上記の出力からわかるように、「body」の 2 番目のインスタンスでは、シリアライゼーションが生成されます。

 <body>&lt;rootTag&gt;&lt;body&gt;All systems online.&lt;/body&gt;&lt;/rootTag&gt;</body>

それ以外の

<body><rootTag><body>All systems online.</body></rootTag></body>

この問題を解決するには?

4

3 に答える 3

7

注: 私はEclipseLink JAXB (MOXy)のリーダーであり、JAXB (JSR-222)エキスパート グループのメンバーです。

このユース ケースは、@XmlAnyElementアノテーションを使用してDOMHandler. JAXB RI でこれを行うとバグが発生するようですが、次の使用例は EclipseLink JAXB (MOXy) で機能します。

BodyDomHandler

デフォルトでは、JAXB 実装はマップされていないコンテンツを DOM ノードとして表します。を DOM の別の表現に利用できますDomHandler。この場合、DOM を として表現しますString

import java.io.*;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.annotation.DomHandler;
import javax.xml.transform.Source;
import javax.xml.transform.stream.*;

public class BodyDomHandler implements DomHandler<String, StreamResult> {

    private static final String BODY_START_TAG = "<body>";
    private static final String BODY_END_TAG = "</body>";

    private StringWriter xmlWriter = new StringWriter();

    public StreamResult createUnmarshaller(ValidationEventHandler errorHandler) {
        return new StreamResult(xmlWriter);
    }

    public String getElement(StreamResult rt) {
        String xml = rt.getWriter().toString();
        int beginIndex = xml.indexOf(BODY_START_TAG) + BODY_START_TAG.length();
        int endIndex = xml.indexOf(BODY_END_TAG);
        return xml.substring(beginIndex, endIndex);
    }

    public Source marshal(String n, ValidationEventHandler errorHandler) {
        try {
            String xml = BODY_START_TAG + n.trim() + BODY_END_TAG;
            StringReader xmlReader = new StringReader(xml);
            return new StreamSource(xmlReader);
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

}

メッセージ

以下は、クラスで@XmlAnyElement注釈を指定する方法です。Message

import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlType;

@XmlType(propOrder = { "body" })
public class Message
{
    private String body;
    public String getBody() { return body; }
    @XmlAnyElement(BodyDomHandler.class)
    public void setBody(String body) { this.body = body; }
}

出力

以下は、あなたの実行からの出力ですSerialziationTest

<?xml version="1.0" encoding="UTF-8"?>
<Report>
   <message>
      <body>Sample report message.</body>
   </message>
</Report>
<?xml version="1.0" encoding="UTF-8"?>
<Report>
   <message>
      <body>
         <rootTag>
            <body>All systems online.</body>
         </rootTag>
      </body>
   </message>
</Report>

詳細については

注 - JAXB RI のバグ

JAXB 参照実装にバグがあるようで、サンプル コードは次のようなスタック トレースになります。

Exception in thread "main" javax.xml.bind.MarshalException
 - with linked exception:
[com.sun.istack.internal.SAXException2: unable to marshal type "java.lang.String" as an element because it is missing an @XmlRootElement annotation]
    at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:317)
    at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:243)
    at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:75)
    at forum12428727.SerializationTest.main(SerializationTest.java:20)
Caused by: com.sun.istack.internal.SAXException2: unable to marshal type "java.lang.String" as an element because it is missing an @XmlRootElement annotation
    at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:216)
    at com.sun.xml.internal.bind.v2.runtime.LeafBeanInfoImpl.serializeRoot(LeafBeanInfoImpl.java:126)
    at com.sun.xml.internal.bind.v2.runtime.property.SingleReferenceNodeProperty.serializeBody(SingleReferenceNodeProperty.java:100)
    at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:306)
    at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:664)
    at com.sun.xml.internal.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:141)
    at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:306)
    at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:561)
    at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:290)
    at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:462)
    at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:314)
    ... 3 more
于 2012-09-14T17:22:40.240 に答える
2

3つの異なるソリューション1)、2)3)、以下:

1)次の投稿は、ソリューションLoreshの説明です:

http://anna-safronova.livejournal.com/2524.html?thread=9180

これにはまだ制限の詳細がありません。

  • 埋め込み html では、<![CDATAブロックが必要です

  • JAXB の依存関係

com.sun.xml.bind.marshaller.CharacterEscapeHandler

コンパイルのために jaxb-impl をインポートする必要があります / 実行のために必要になる場合があります。

<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>  
 <version>2.2.4</version>
  • 制限 : このソリューションはコンテナ固有のものであり、クラスローディング ポリシーが原因で実行されない場合があります。

2) 別の同様のアプローチは、JDK の rt.jar 依存関係です。

com.sun.xml.internal.bind.CharacterEscapeHandler

http://theopentutorials.com/tutorials/java/jaxb/jaxb-marshalling-and-unmarshalling-cdata-block/

同じ制限/ターゲットJDKに依存し、Eclipse/Mavenのいくつかの調整が必要です(悪い代替/私の意見)

3) 最後に、Reg Whitton の別の投稿で最良の解決策が見つかりました。

https://stackoverflow.com/a/12637295/560410

これは詳細なレシピです:

http://javacoalface.blogspot.co.uk/2012/09/outputting-cdata-sections-with-jaxb.html

私にとって完璧に機能しました!

于 2013-08-29T07:04:58.613 に答える