web-dev-qa-db-fra.com

JAXB Marshalling Unmarshalling avec CDATA

j'essaie de faire du marshaling avec JAXB. 

ma sortie est comme

<?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>

mais j'ai besoin de sortie comme

<?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>

J'utilise le code suivant pour le faire. et si je décode le code, j'obtiens une exception de liaison de propriété. Sans cela, je peux compiler mais je ne reçois pas la sortie exacte requise.

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

et mes haricots aiment

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

Classe d'adaptateur

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

Vous pouvez faire ce qui suit:

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

}

Racine

L'annotation @XmlJavaTypeAdapter permet d'indiquer que la variable XmlAdapter doit être utilisée.

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;

}

Démo

J'ai dû envelopper System.out dans une OutputStreamWriter pour obtenir l'effet souhaité. Notez également que la définition de CharacterEscapeHandler signifie qu'il est responsable de toutes les opérations d'évacuation pour cette 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/Sortie

<?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>
36
Blaise Doughan

Remarque: Je suis le EclipseLink JAXB (MOXy) lead et membre du groupe JAXB (JSR-222) .

Si vous utilisez MOXy en tant que fournisseur JAXB (JSR-222), vous pouvez utiliser l'extension @XmlCDATA pour votre cas d'utilisation.

Racine

L'annotation @XmlCDATA sert à indiquer que vous souhaitez que le contenu d'un champ/propriété soit encapsulé dans une section CDATA. L'annotation @XmlCDATA peut être utilisée en combinaison avec @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

Pour utiliser MOXy en tant que fournisseur JAXB, vous devez ajouter le fichier nommé jaxb.properties avec l'entrée suivante.

javax.xml.bind.context.factory=org.Eclipse.persistence.jaxb.JAXBContextFactory

Démo

Vous trouverez ci-dessous un code de démonstration permettant de prouver que tout fonctionne.

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

Ci-dessous se trouvent l'entrée et la sortie de l'exécution du code de démonstration.

<?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>

Pour plus d'informations

11
Blaise Doughan

Désolé de creuser cette question et d’afficher une nouvelle réponse (mon représentant n’est pas encore assez haut pour faire un commentaire ...) . J'ai rencontré le même problème, j’ai essayé la réponse de Blaise Doughan, mais d'après mes tests, cela ne couvre pas tous les cas, soit je fais quelque chose de mal quelque part.



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

De mes tests, ce code supprime tous les échappements, peu importe si vous utilisez l'annotation @XmlJavaTypeAdapter(AdapterCDATA.class) sur votre attribut ...

Pour résoudre ce problème, j'ai implémenté la CharacterEscapeHandler suivante:


 Classe publique CDataAwareUtfEncodedXmlCharacterEscapeHandler implémente CharacterEscapeHandler {

 caractère final statique privé [] cDataPrefix = "<! [CDATA [". toCharArray (); 
 caractère final statique privé [] cDataSuffix = "]]>". toCharArray (); 

 public final final CDataAwareUtfEncodedXmlCharacterEscapeHandler instance = new CDataAwareUtfEncodedXmlCharacterEscapeHandler (); 

 private CDataAwareUtfEncodedXmlCharacterEscapeHandler () {
 } 

 @Passer outre
 public void escape (char [] ch, int start, int length, boolean isAttVal, Writer out) jette IOException {
 booléen isCData = longueur> cDataPrefix.length + cDataSuffix.length; 
 if (isCData) {
 pour (int i = 0, j = début; i <cDataPrefix.length; ++ i, ++ j) {
 if (cDataPrefix [i]! = ch [j]) {
 isCData = false; 
 Pause;
 } 
 } 
 if (isCData) {
 pour (int i = cDataSuffix.length - 1, j = début + longueur - 1; i> = 0; --i, --j) {
 if (cDataSuffix [i]! = ch [j]) {
 isCData = false; 
 Pause;
 } 
 } 
 } 
 } 
 if (isCData) {
 out.write (ch, start, length); 
 } autre {
 MinimumEscapeHandler.theInstance.escape (ch, début, longueur, isAttVal, out); 
 } 
 } 
 } 

Si votre codage n'est pas UTF *, vous ne voudrez peut-être pas appeler MinimumEscapeHandler, mais plutôt NioEscapeHandler ou même DumbEscapeHandler.

3
Marc P.

com.Sun.internal ne fonctionne pas avec play2, mais cela fonctionne

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 Marinho
    @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

J'ai atterri sur cette page en essayant de trouver une solution à un problème similaire, j'ai trouvé une autre approche pour résoudre ce problème. Un moyen de résoudre ce problème consiste à envoyer le XML en tant qu’événements SAX2 à un gestionnaire, puis à écrire la logique dans le gestionnaire afin d’ajouter les balises CDATA au XML. Cette approche ne nécessite aucune annotation. Utile dans les scénarios où les classes à marshaler sont générées à partir de fichiers XSD.

Supposons que vous ayez un champ String dans une classe générée à partir de XSD qui doit être marshalé et que le champ String contient des caractères spéciaux qui doivent être placés dans une balise CDATA. 

@XmlRootElement
public class TestingCDATA{
    public String xmlContent;

}

Nous commencerons par rechercher une classe appropriée dont la méthode peut être remplacée dans notre gestionnaire de contenu. Une de ces classes est XMLWriter présente dans le package com.Sun.xml.txw2.output. Elle est disponible dans jdk 1.7 et 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();
        }
    }
}

Nous substituons la méthode des caractères, en utilisant regex pour vérifier si des caractères spéciaux sont contenus. S'ils sont trouvés, nous mettons des balises CDATA autour d'eux. Dans ce cas, XMLWriter se charge d’ajouter une balise CDATA.

Nous utiliserons le code suivant pour marshaling: 

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

Cela permettrait de marshaler l'objet et de renvoyer XML, si nous transmettons une requête à marshaler, comme indiqué ci-dessous.

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