2010-11-19 35 views
5

Je voudrais modifier le flux de sortie pour une opération de marshaling JAXB pour inclure du code XML arbitraire. Voici un exemple pour clarifier la situation.Comment puis-je modifier le flux de sortie de marshaling JAXB pour inclure du code XML arbitraire?

J'ai un objet de domaine Product arbitraire avec annotations JAXB qui ressemblent actuellement comme ceci:

@XmlRootElement(name="Product") 
public class Product { 

    @XmlElement(name="CommonProperty") 
    private String commonProperty="Something"; 

    @XmlElement(name="ExtraXml") 
    private String extraXml="Something extra"; 

} 

Ce qui serait typiquement maréchal en ceci:

<Product> 
    <CommonProperty>Something</CommonProperty> 
    <ExtraXml>Something else</ExtraXml> 
</Product> 

Maintenant, si le champ extraXml contenait des XML supplémentaire (de complexité arbitraire) qui devait être inclus en ligne avec le résultat final marshalled?

Say, extraXml contenait « <abc><def>Something extra</def></abc> », je voudrais vraiment une solution qui m'a permis de maréchal Product comme celui-ci (formatage en option):

<Product> 
    <CommonProperty>Something</CommonProperty> 
    <abc> 
    <def>Something extra</def> 
    </abc> 
</Product> 

Je l'ai regardé this related question mais il n'a pas tout à fait céder le résultat que je suis après, car il semble plus orienté vers un changement de format global plutôt que l'insertion DOM.

La propriété extraXml est juste là pour l'illustration, elle peut être marquée comme @XmlTransient ou dans une seule classe spécialisée. Le seul critère est qu'il peut en quelque sorte obtenir un String contenant un contenu XML complètement arbitraire à ajouter à la sortie marshalled Product.

Je devrais également mentionner que les consommateurs de la sortie de celui-ci sont en mesure d'analyser le contenu arbitraire d'une manière qui leur convient. Le but ici est de simplifier le traitement côté serveur.

Merci d'avance pour toute aide que vous pouvez offrir.

Répondre

6

Vous pouvez représenter le XML supplémentaire en tant que noeud DOM au lieu d'un String.

Modifiez la propriété extraXML pour qu'elle soit un org.w3c.dom.Node au lieu d'une chaîne et annotez-la avec @XmlAnyElement.

import javax.xml.bind.annotation.XmlAccessType; 
import javax.xml.bind.annotation.XmlAccessorType; 
import javax.xml.bind.annotation.XmlAnyElement; 
import javax.xml.bind.annotation.XmlElement; 
import javax.xml.bind.annotation.XmlRootElement; 

import org.w3c.dom.Node; 

@XmlRootElement(name="Product") 
@XmlAccessorType(XmlAccessType.FIELD) 
public class Product { 

    @XmlElement(name="CommonProperty") 
    private String commonProperty="Something"; 

    @XmlAnyElement 
    private Node extraXml; 

} 

Ensuite, lorsque vous umarshal un document XML tel que:

<Product> 
    <CommonProperty>Something</CommonProperty> 
    <abc> 
    <def>Something extra</def> 
    </abc> 
</Product> 

Avec le code suivant:

import java.io.File; 
import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Marshaller; 
import javax.xml.bind.Unmarshaller; 

public class Demo { 

    public static void main(String[] args) throws Exception { 
     JAXBContext jc = JAXBContext.newInstance(Product.class); 

     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     File xml = new File("input.xml"); 
     Product product = (Product) unmarshaller.unmarshal(xml); 

     Marshaller marshaller = jc.createMarshaller(); 
     marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
     marshaller.marshal(product, System.out); 
    } 
} 

Le "XML supplémentaire" sera conservé comme un nœud DOM.

+0

Sons prometteurs - pourriez-vous élaborer un peu? –

+0

@Gary, j'ai mis à jour ma réponse pour expliquer comment cela peut être fait en utilisant @XmlAnyElement. –

+1

+1 pour votre mise à jour majeure - c'est une aide précieuse (et félicitations pour avoir brisé 3K aujourd'hui). Il y a un dernier bout si cela ne vous dérange pas. Au début du marshalling, j'ai seulement le 'extraXml' comme une chaîne et une instance de' Product' créé par 'new Product()'. Existe-t-il un moyen de spécifier le nœud d'une manière que JAXB est heureux de rassembler? –

5

Avec l'aide de Blaise Doughan voici la solution finale sur laquelle je me suis arrêté. Cela inclut un code de démonstration supplémentaire pour aider les autres qui peuvent se trouver dans cette situation.

Les classes

ProductList (une nouvelle classe d'emballage pour montrer comment cela fonctionne pour plusieurs entrées de produit)

import javax.xml.bind.annotation.XmlElement; 
import javax.xml.bind.annotation.XmlElementWrapper; 
import javax.xml.bind.annotation.XmlRootElement; 
import java.util.ArrayList; 
import java.util.List; 

@XmlRootElement(name="ProductList") 
public class ProductList { 

    @XmlElementWrapper(name="Products") 
    @XmlElement(name="Product") 
    public List<Product> products = new ArrayList<Product>(); 

} 

Produit (avec les modifications de Blaise)

import org.w3c.dom.Node; 

import javax.xml.bind.annotation.*; 

@XmlRootElement(name="Product") 
@XmlAccessorType(XmlAccessType.FIELD) 
public class Product { 

    @XmlElement(name="CommonProperty") 
    public String commonProperty="Something"; 

    @XmlAnyElement 
    public Node extraXml; 

} 

Main (certains code de démonstration)

import org.w3c.dom.Document; 
import org.xml.sax.InputSource; 

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Marshaller; 
import javax.xml.parsers.DocumentBuilderFactory; 
import java.io.StringReader; 

public class Main { 

    public static void main(String[] args) throws Exception { 

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 

    // Build some arbitrary extra XML and prepare an InputStream 
    String fragment1 = "<abc><def>Some extra 1</def></abc>"; 
    String fragment2 = "<ghi><jkl>Some extra 2</jkl></ghi>"; 
    Document document1 = factory.newDocumentBuilder().parse(new InputSource(new StringReader(fragment1))); 
    Document document2 = factory.newDocumentBuilder().parse(new InputSource(new StringReader(fragment2))); 

    Product product1 = new Product(); 
    product1.commonProperty = "Hello 1"; 
    product1.extraXml=document1.getFirstChild(); 

    Product product2 = new Product(); 
    product2.commonProperty = "Hello 2"; 
    product2.extraXml=document2.getFirstChild(); 

    ProductList productList = new ProductList(); 
    productList.products.add(product1); 
    productList.products.add(product2); 

    JAXBContext jc = JAXBContext.newInstance(ProductList.class, Product.class); 
    Marshaller marshaller = jc.createMarshaller(); 
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
    marshaller.marshal(productList, System.out); 

    } 
} 

La sortie finale:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<ProductList> 
    <Products> 
     <Product> 
      <CommonProperty>Hello 1</CommonProperty> 
      <abc> 
       <def>Some extra 1</def> 
      </abc> 
     </Product> 
     <Product> 
      <CommonProperty>Hello 2</CommonProperty> 
      <ghi> 
       <jkl>Some extra 2</jkl> 
      </ghi> 
     </Product> 
    </Products> 
</ProductList> 

Résultat!

Mais il ne fonctionne pas dans JBoss ...

Si vous essayez ci-dessus dans JBoss 4.2.3.GA ou 4.3, vous pouvez constater que vous obtenez un

class com.sun.org.apache.xerces.internal.dom.DocumentFragmentImpl nor any of its super class is known to this context. 

exception être signalé. Cela est (probablement) dû au xercesImpl.jar dans les dossiers JBoss lib et /lib/endorsed utilisant la fonction de remplacement Java META-INF/Services pour empêcher le JDK de marshaler en utilisant des classes internes. Vous devrez peut-être spécifier un autre DocumentBuilderFactory directement en utilisant l'approche suivante:

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance("oracle.xml.jaxp.JXDocumentBuilderFactory", this 
     .getClass().getClassLoader()); 

La mise en œuvre Oracle semble atténuer ces problèmes, peut-être parce qu'il garde la conscience des classes Node DOM dans le contexte JAXB. Ces classes se trouvent dans les fichiers xdb.jar et xmlparserv2.jar fournis avec le client Oracle.

Espérons que ça aide.