41

JAXB を使用してデータを XML にシリアライズしています。クラスコードは以下のように簡単です。Args の値の CDATA ブロックを含む XML を生成したいと考えています。たとえば、現在のコードは次の XML を生成します。

<command>
   <args>
      <arg name="test_id">1234</arg>
      <arg name="source">&lt;html>EMAIL&lt;/html></arg>
   </args>
</command>

以下のように、「ソース」引数を CDATA でラップしたいと思います。

<command>
   <args>
      <arg name="test_id">1234</arg>
      <arg name="source"><[![CDATA[<html>EMAIL</html>]]></arg>
   </args>
</command>

以下のコードでこれを達成するにはどうすればよいですか?

@XmlRootElement(name="command")
public class Command {

        @XmlElementWrapper(name="args")
        protected List<Arg>  arg;
    }
@XmlRootElement(name="arg")
public class Arg {

        @XmlAttribute
        public String name;
        @XmlValue
        public String value;

        public Arg() {};

        static Arg make(final String name, final String value) {
            Arg a = new Arg();
            a.name=name; a.value=value;
            return a; }
    }
4

10 に答える 10

26

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

MOXy を JAXB プロバイダーとして使用している場合は、@XmlCDATA拡張機能を利用できます。

package blog.cdata;

import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlCDATA;

@XmlRootElement(name="c")
public class Customer {

   private String bio;

   @XmlCDATA
   public void setBio(String bio) {
      this.bio = bio;
   }

   public String getBio() {
      return bio;
   }

}

詳細については

于 2010-07-15T00:09:48.537 に答える
20

JAXB を使用Marshaller#marshal(ContentHandler)してオブジェクトにマーシャリングしContentHandlerます。characters使用している ContentHandler 実装 (JDOMSAXHandlerや ApacheXMLSerializerなど)のメソッドをオーバーライドするだけです。

public class CDataContentHandler extends (SAXHandler|XMLSerializer|Other...) {
    // 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();
    }
}

これは、要素のリストをハードコーディングする必要がないため、メソッドを使用するよりもはるかに優れています。必要な場合にのみXMLSerializer.setCDataElements(...)CDATA ブロックを自動的に出力します。

于 2011-07-25T21:51:31.383 に答える
17

ソリューションのレビュー:

  • fred の答えは、文字列リテラルのみを変更し、CDATA セクションを作成しないため、マーシャラーがスキーマにリンクされている場合にコンテンツの検証中に失敗する回避策にすぎません。したがって、文字列をfooから<![CDATA[foo]]>に書き換えるだけの場合、文字列の長さは Xerces によって 3 ではなく 15 で認識されます。
  • MOXy ソリューションは実装固有であり、JDK のクラスだけでは機能しません。
  • 非推奨の XMLSerializer クラスへの getSerializer 参照を使用したソリューション。
  • 解決策 LSSerializer はただの苦痛です。

XMLStreamWriter実装を使用して、a2ndrade のソリューションを変更しました。このソリューションは非常にうまく機能します。

XMLOutputFactory xof = XMLOutputFactory.newInstance();
XMLStreamWriter streamWriter = xof.createXMLStreamWriter( System.out );
CDataXMLStreamWriter cdataStreamWriter = new CDataXMLStreamWriter( streamWriter );
marshaller.marshal( jaxbElement, cdataStreamWriter );
cdataStreamWriter.flush();
cdataStreamWriter.close();

それが CDataXMLStreamWriter の実装です。委任クラスは、すべてのメソッド呼び出しを特定の XMLStreamWriter 実装に委任するだけです。

import java.util.regex.Pattern;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

/**
 * Implementation which is able to decide to use a CDATA section for a string.
 */
public class CDataXMLStreamWriter extends DelegatingXMLStreamWriter
{
   private static final Pattern XML_CHARS = Pattern.compile( "[&<>]" );

   public CDataXMLStreamWriter( XMLStreamWriter del )
   {
      super( del );
   }

   @Override
   public void writeCharacters( String text ) throws XMLStreamException
   {
      boolean useCData = XML_CHARS.matcher( text ).find();
      if( useCData )
      {
         super.writeCData( text );
      }
      else
      {
         super.writeCharacters( text );
      }
   }
}
于 2012-05-05T09:59:59.850 に答える
11

上記のサイトで参照されているコードサンプルは次のとおりです。

import java.io.File;
import java.io.StringWriter;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Document;

public class JaxbCDATASample {

    public static void main(String[] args) throws Exception {
        // unmarshal a doc
        JAXBContext jc = JAXBContext.newInstance("...");
        Unmarshaller u = jc.createUnmarshaller();
        Object o = u.unmarshal(...);

        // create a JAXB marshaller
        Marshaller m = jc.createMarshaller();

        // get an Apache XMLSerializer configured to generate CDATA
        XMLSerializer serializer = getXMLSerializer();

        // marshal using the Apache XMLSerializer
        m.marshal(o, serializer.asContentHandler());
    }

    private static XMLSerializer getXMLSerializer() {
        // configure an OutputFormat to handle CDATA
        OutputFormat of = new OutputFormat();

        // specify which of your elements you want to be handled as CDATA.
        // The use of the '^' between the namespaceURI and the localname
        // seems to be an implementation detail of the xerces code.
        // When processing xml that doesn't use namespaces, simply omit the
        // namespace prefix as shown in the third CDataElement below.
        of.setCDataElements(
            new String[] { "ns1^foo",   // <ns1:foo>
                   "ns2^bar",   // <ns2:bar>
                   "^baz" });   // <baz>

        // set any other options you'd like
        of.setPreserveSpace(true);
        of.setIndenting(true);

        // create the serializer
        XMLSerializer serializer = new XMLSerializer(of);
        serializer.setOutputByteStream(System.out);

        return serializer;
    }
}
于 2010-07-17T20:11:44.597 に答える
10

Michael Ernst と同じ理由で、ここでの回答のほとんどに満足できませんでした。raigltorfer の OutputFormat ソリューションのように、CDATA タグを定義済みのフィールド セットに配置する必要があったため、彼のソリューションを使用できませんでした。

私の解決策は、DOM ドキュメントにマーシャリングしてから、null XSL 変換を実行して出力を行うことです。トランスフォーマーを使用すると、どの要素を CDATA タグでラップするかを設定できます。

Document document = ...
jaxbMarshaller.marshal(jaxbObject, document);

Transformer nullTransformer = TransformerFactory.newInstance().newTransformer();
nullTransformer.setOutputProperty(OutputKeys.INDENT, "yes");
nullTransformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, "myElement {myNamespace}myOtherElement");
nullTransformer.transform(new DOMSource(document), new StreamResult(writer/stream));

詳細はこちら: http://javacoalface.blogspot.co.uk/2012/09/outputting-cdata-sections-with-jaxb.html

于 2012-09-28T09:29:47.577 に答える
6

次の簡単な方法は、CDATA をネイティブにサポートしていない JAX-B に CDATA サポートを追加します。

  1. カスタム単純型 CDataString拡張文字列を宣言して、CDATA を介して処理する必要があるフィールドを識別します
  2. CDataString のコンテンツを解析して出力するカスタム CDataAdapterを作成する
  3. JAXB バインディングを使用して CDataString と CDataAdapter をリンクします。CdataAdapter は、マーシャル/アンマーシャル時に CdataStrings に追加/CdataStrings から/から削除します。
  4. CDATA 文字列を出力するときに文字をエスケープしないカスタム文字エスケープ ハンドラを宣言し、これを Marshaller CharacterEscapeEncoder として設定します。

ほら、どの CDataString 要素もマーシャル時にカプセル化されます。非整列化時に、 は自動的に削除されます。

于 2011-08-04T21:13:26.003 に答える
4

@a2ndradeさんの回答の補足。

JDK 8 で拡張するクラスが 1 つ見つかりました。ただし、そのクラスはcom.sunパッケージに含まれていることに注意してください。このクラスが将来のJDKで削除される可能性がある場合に備えて、コードのコピーを1つ作成できます。

public class CDataContentHandler extends com.sun.xml.internal.txw2.output.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();
    }
  }
}

使い方:

  JAXBContext jaxbContext = JAXBContext.newInstance(...class);
  Marshaller marshaller = jaxbContext.createMarshaller();
  StringWriter sw = new StringWriter();
  CDataContentHandler cdataHandler = new CDataContentHandler(sw,"utf-8");
  marshaller.marshal(gu, cdataHandler);
  System.out.println(sw.toString());

結果の例:

<?xml version="1.0" encoding="utf-8"?>
<genericUser>
  <password><![CDATA[dskfj>><<]]></password>
  <username>UNKNOWN::UNKNOWN</username>
  <properties>
    <prop2>v2</prop2>
    <prop1><![CDATA[v1><]]></prop1>
  </properties>
  <timestamp/>
  <uuid>cb8cbc487ee542ec83e934e7702b9d26</uuid>
</genericUser>
于 2016-09-18T12:28:25.390 に答える
2

Xerxes-J 2.9 の時点で、XMLSerializer は非推奨になりました。これを DOM Level 3 LSSerializer または JAXP の Transformation API for XML に置き換えることをお勧めします。誰かがアプローチを試みましたか?

于 2011-05-05T15:43:08.557 に答える
1
于 2016-05-12T10:14:39.160 に答える