2011-03-15 12 views
5

Estoy tratando de ordenar/deshacer de dos archivos XML diferentes a POJOS. El primer archivo XML es el siguiente:Uso de JAXB para hacer una referencia cruzada de XmlID desde dos archivos XML

--Network.xml-- 
<Network> 
    <Nodes> 
    <Node id="ROD" /> 
    <Node id="KFI" /> 
    <Node id="JND" /> 
    </Nodes> 
    <Arcs> 
    <Arc fromNode="ROD" /> 
    <Arc fromNode="JND" /> 
    </Arcs> 
</Network> 
--------- 

Usando @XmlID y @XmlIDREF anotaciones, puedo llenar con éxito las clases de arco para que apunte al nodo correcto, que hace referencia.

Sin embargo, también tiene que analizar este XML:

--NetworkInputs.xml-- 
<NetworkInputs> 
    <Flows> 
    <Flow toNode="JND" /> 
    <Flow toNode="ROD" /> 
    </Flows> 
</NetworkInputs> 
------ 

Actualmente, mi programa descoloca el objeto de red con éxito, pero no hay conexión entre la red y NetworkInputs que permite JAXB a "ver" los nodos que existen en red. Quiero que mis objetos Flow apunten al nodo correcto en la clase de red.

Básicamente quiero hacer esto: http://old.nabble.com/JAXB-Unmarshalling-and-XmlIDREF-using-different-stores-td14035248.html

he intentado implementar esto: http://weblogs.java.net/blog/kohsuke/archive/2005/08/pluggable_ididr.html y simplemente no funciona, porque no puedo obtener los datos de nodo para mi red poblada desde un contexto estático .

¿Es posible hacer algo como esto?

Respuesta

8

Esto se puede hacer con un XmlAdapter. El truco es tendrá que ser inicializado con todos los nodos de Network.xml y se pasa al Unmarshaller se utiliza con el NetworkInputs.xml XmlAdapter:

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(Network.class, NetworkInputs.class); 

     File networkXML = new File("Network.xml"); 
     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     Network network = (Network) unmarshaller.unmarshal(networkXML); 

     File networkInputsXML = new File("NetworkInputs.xml"); 
     Unmarshaller unmarshaller2 = jc.createUnmarshaller(); 
     NodeAdapter nodeAdapter = new NodeAdapter(); 
     for(Node node : network.getNodes()) { 
      nodeAdapter.getNodes().put(node.getId(), node); 
     } 
     unmarshaller2.setAdapter(nodeAdapter); 
     NetworkInputs networkInputs = (NetworkInputs) unmarshaller2.unmarshal(networkInputsXML); 

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

El truco consiste en asignar la propiedad toNode de flujo con una XmlAdapter :

import javax.xml.bind.annotation.XmlAttribute; 
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 

public class Flow { 

    private Node toNode; 

    @XmlAttribute 
    @XmlJavaTypeAdapter(NodeAdapter.class) 
    public Node getToNode() { 
     return toNode; 
    } 

    public void setToNode(Node toNode) { 
     this.toNode = toNode; 
    } 

} 

El adaptador tendrá el siguiente aspecto. El truco es que vamos a pasar una XmlAdapter configurado que sepa acerca de todos los nodos al unmarshaller:

import java.util.HashMap; 
import java.util.Map; 

import javax.xml.bind.annotation.adapters.XmlAdapter; 

public class NodeAdapter extends XmlAdapter<String, Node>{ 

    private Map<String, Node> nodes = new HashMap<String, Node>(); 

    public Map<String, Node> getNodes() { 
     return nodes; 
    } 

    @Override 
    public Node unmarshal(String v) throws Exception { 
     return nodes.get(v); 
    } 

    @Override 
    public String marshal(Node v) throws Exception { 
     return v.getId(); 
    } 

} 
+0

Esto funcionó. Gracias, realmente lo aprecio! –

+1

Great Great Idea, deberías publicarlo en tu blog. – ekeren

0

Mi solución: la resolución de ID es manejado por un (por desgracia) de clases interna (com.sun.xml. internal.bind.IDResolver) que se puede establecer desde externo.

final Unmarshaller unmarshaller = context.createUnmarshaller(); 
unmarshaller.setProperty(IDResolver.class.getName(), resolver); 

Donde el resolver se puede usar sobre muchas inststance del unmarshaller. Pero el punto es que la resolución no se borrará itsself en startDocument como la implementación predeterminada de com.sun.xml.internal.bind.v2.runtime.unmarshaller.DefaultIDResolver hace:

import java.text.MessageFormat; 
import java.util.HashMap; 
import java.util.Map; 
import java.util.concurrent.Callable; 

import org.xml.sax.SAXException; 

import com.sun.xml.internal.bind.IDResolver; 

public final class IDResolverExtension extends IDResolver { 
    public static final class CallableImplementation implements Callable<Object> { 
     private final Object value; 

     private CallableImplementation(final Object value) { 
      this.value = value; 
     } 

     @Override 
     public Object call() { 
      return value; 
     } 
    } 

    private final Map<KeyAndClass, Object> m = new HashMap<KeyAndClass, Object>(); 

    @SuppressWarnings("rawtypes") 
    @Override 
    public synchronized CallableImplementation resolve(final String key0, final Class clazz) throws SAXException { 
     assert clazz != null; 
     assert key0 != null; 
     final KeyAndClass key = new KeyAndClass(clazz, key0); 
     final Object value = m.get(key); 
     return new CallableImplementation(value); 
    } 

    static class KeyAndClass { 
     public final Class<?> clazz; 
     public final String key; 

     public KeyAndClass(final Class<?> clazz, final String key) { 
      this.clazz = clazz; 
      this.key = key; 
     } 

     @Override 
     public int hashCode() { 
      final int prime = 31; 
      int result = 1; 
      result = prime * result + clazz.hashCode(); 
      result = prime * result + key.hashCode(); 
      return result; 
     } 

     @Override 
     public boolean equals(Object obj) { 
      if (this == obj) { 
       return true; 
      } 
      if (obj == null) { 
       return false; 
      } 
      if (getClass() != obj.getClass()) { 
       return false; 
      } 
      final KeyAndClass other = (KeyAndClass) obj; 
      if (!clazz.equals(other.clazz)) { 
       return false; 
      } 
      if (!key.equals(other.key)) { 
       return false; 
      } 
      return true; 
     } 

    } 

    @Override 
    public synchronized void bind(final String key0, final Object value) throws SAXException { 
     assert key0 != null; 
     assert value != null; 
     Class<? extends Object> clazz = value.getClass(); 
     assert clazz != null; 
     final KeyAndClass key = new KeyAndClass(clazz, key0); 
     final Object oldValue = m.put(key, value); 
     if (oldValue != null) { 
      final String message = MessageFormat.format("duplicated key ''{0}'' => ''{1}'' - old: ''{2}''", key, value, 
        oldValue); 
      throw new AssertionError(message); 
     } 
    } 
} 
Cuestiones relacionadas