2010-11-19 20 views
5

Me gustaría modificar la secuencia de salida para una operación de clasificación JAXB para incluir algún XML arbitrario. Aquí hay un ejemplo para aclarar la situación.¿Cómo puedo modificar la secuencia de salida de clasificación JAXB para incluir arbitrariamente XML en línea?

que tienen un objeto arbitrario Product dominio con anotaciones JAXB que en la actualidad se parece a esto:

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

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

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

} 

que normalmente sería reunir en esto:

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

Ahora, lo que si el campo extraXml contenía algunos XML adicional (de complejidad arbitraria) que debía incluirse en línea con el resultado final ordenado?

Say, extraXml contenía "<abc><def>Something extra</def></abc>", realmente me gustaría una solución que me permitió reunir Product como esto (el formato opcional):

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

He mirado en this related question pero no lo hizo bastante arrojo el resultado que estoy buscando, ya que parece estar más orientado hacia un cambio de formato general en lugar de la inserción DOM.

La propiedad extraXml está simplemente ahí para la ilustración, podría marcarse como @XmlTransient o en una sola clase especializada. El único criterio es que de alguna manera puede obtener un String que contenga contenido XML completamente arbitrario para anexar a la salida almacenada individual Product.

También debo mencionar que los consumidores de la salida de esto son capaces de analizar el contenido arbitrario de la manera que les convenga. El propósito aquí es simplificar el procesamiento en el lado del servidor.

Gracias de antemano por cualquier ayuda que pueda ofrecer.

Respuesta

6

Puede representar el XML adicional como un nodo DOM en lugar de una cadena.

Cambie la propiedad extraXML para que sea org.w3c.dom.Node en lugar de String y anótelo con @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; 

} 

Luego, cuando se umarshal un documento XML como por ejemplo:

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

con el siguiente código:

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

El "XML extra" se mantendrá como un nodo DOM.

+0

Suena prometedor: ¿podría explicar un poco? –

+0

@Gary, he actualizado mi respuesta para explicar cómo se puede hacer esto utilizando @XmlAnyElement. –

+1

+1 para su actualización principal: es una gran ayuda (y felicidades por haber roto 3K hoy). Hay una última parte si no te importa. Al comienzo de la clasificación, solo tengo 'extraXml' como cadena y una instancia de' Product' creada por 'new Product()'. ¿Hay alguna manera de especificar el Nodo de una manera que JAXB esté feliz de organizar? –

5

Con Blaise Doughan la ayuda aquí es la solución final en la que me decidí. Esto incluye un código de demostración adicional para ayudar a otros que puedan encontrarse en esta situación.

Las clases

la lista de productos (una nueva clase de envoltura para mostrar cómo esto funciona para múltiples entradas de productos)

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

} 

Producto (con modificaciones 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; 

} 

Principal (algunos código de demostración)

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 salida final:

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

Resultado!

Pero no funciona en JBoss ...

Si intenta lo anterior en JBoss 4.2.3.GA o 4.3 es posible que se obtiene una excepción

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

siendo informado. Esto es (probablemente) debido a xercesImpl.jar en las carpetas JBoss lib y /lib/endorsed que utilizan el recurso de anulación Java META-INF/Services para evitar que el JDK se organice mediante clases internas. Es posible que tenga que especificar una alternativa DocumentBuilderFactory directamente utilizando el siguiente enfoque:

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

La implementación de Oracle parece aliviar estos problemas, tal vez debido a que mantiene la conciencia de las clases DOM nodo dentro del contexto JAXB. Estas clases se encuentran en los archivos xdb.jar y xmlparserv2.jar enviados con el cliente de Oracle.

Espero que ayude.

Cuestiones relacionadas