40 votes

Comment générer un bloc CDATA en utilisant JAXB?

J'utilise JAXB pour sérialiser mes données en XML. Le code de classe est simple, comme indiqué ci-dessous. Je veux produire du XML contenant des blocs CDATA pour la valeur de certains Args. Par exemple, le code actuel produit ce XML:

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

Je veux envelopper le "source" arg dans CDATA tel qu'il ressemble à ci-dessous:

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

Comment puis-je y parvenir dans le code ci-dessous?

 @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; }
    }
 

28voto

Blaise Doughan Points 75613

Note: je suis le EclipseLink JAXB (MOXy) plomb et un membre de la JAXB (JSR-222) du groupe d'experts.

Si vous utilisez MOXy que votre JAXB fournisseur alors vous pouvez tirer profit de l' @XmlCDATA extension.

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

}

Pour Plus D'Informations

20voto

a2ndrade Points 1288

Utilisation JAXB de l' Marshaller#marshal(ContentHandler) de maréchal en ContentHandler objet. Simplement remplacer l' characters méthode sur le gestionnaire de contenu mise en oeuvre que vous utilisez (par exemple, JDOM de l' SAXHandler, Apache XMLSerializer, etc):

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();
  }

}

C'est beaucoup mieux que d'utiliser l' XMLSerializer.setCDataElements méthode (décrite ci-dessus) parce que vous n'avez pas à coder en dur une liste d'éléments. Il transmet automatiquement CDATA blocs uniquement lorsque celui-ci est requis.

16voto

Michael Ernst Points 61

Évaluation De La Solution:

  • La réponse de fred est juste une solution de contournement qui va échouer lors de la validation du contenu lorsque le Marshaller est lié à un Schéma parce que vous modifiez uniquement la chaîne de caractères littérale et ne pas créer des sections CDATA. Donc, si vous ne la réécriture de la Chaîne de foo à <![CDATA[foo]]> la longueur de la chaîne est reconnue par Xerces avec 15 au lieu de 3.
  • Le MOXy solution est mise en œuvre spécifique et ne fonctionne pas seulement avec les classes du JDK.
  • La solution avec le getSerializer les références à l'obsolète classe XMLSerializer.
  • La solution LSSerializer est juste une douleur.

J'ai modifié la solution de a2ndrade à l'aide d'un XMLStreamWriter mise en œuvre. Cette solution fonctionne très bien.

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

C'est le CDataXMLStreamWriter mise en œuvre. Le délégué de la classe tout simplement les délégués de tous les appels de méthode à la XMLStreamWriter mise en œuvre.

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 );
      }
   }
}

10voto

raiglstorfer Points 1118

Voici l'exemple de code référencé par le site mentionné ci-dessus:

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

9voto

Reg Whitton Points 236

Pour les mêmes raisons que Michael Ernst je n'étais pas très heureux avec la plupart des réponses ici. Je ne pouvais pas utiliser sa solution comme mon exigence était de mettre CDATA balises dans un ensemble défini de domaines - comme dans raiglstorfer de OutputFormat solution.

Ma solution est de maréchal à un document DOM, et puis faire un null transformation XSL pour faire la sortie. Les transformateurs permettent de définir les éléments qui sont enveloppés dans CDATA balises.

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));

Plus d'info ici: http://javacoalface.blogspot.co.uk/2012/09/outputting-cdata-sections-with-jaxb.html

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X