使用JAXB从两个XML文件交叉引用XmlID

我正在尝试将两个不同的XML文件编组/解组到POJOS。 第一个XML文件如下所示:

--Network.xml--            --------- 

使用@XmlID和@XmlIDREF注释,我可以成功填充Arc类以指向它引用的正确节点。

但是,我还必须解析这个XML:

 --NetworkInputs.xml--       ------ 

目前,我的程序成功解组了Network对象,但是Network和NetworkInput之间没有连接,允许JAXB“看到”Network中存在的节点。 我希望我的Flow对象指向Network类中的正确Node。

我基本上想要这样做: http : //old.nabble.com/JAXB-Unmarshalling-and-XmlIDREF-using-different-stores-td14035248.html

我尝试实现这个: http : //weblogs.java.net/blog/kohsuke/archive/2005/08/pluggable_ididr.html它只是不起作用,因为我无法获取我填充的网络的节点数据静态上下文。

甚至可以做这样的事情吗?

这可以使用XmlAdapter完成。 诀窍是需要使用Network.xml中的所有节点初始化XmlAdapter并将其传递给与NetworkInputs.xml一起使用的Unmarshaller:

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

诀窍是使用XmlAdapter映射Flow上的toNode属性:

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

适配器将如下所示。 诀窍是我们将已知的所有节点的已配置XmlAdapter传递给unmarshaller:

 import java.util.HashMap; import java.util.Map; import javax.xml.bind.annotation.adapters.XmlAdapter; public class NodeAdapter extends XmlAdapter{ private Map nodes = new HashMap(); public Map 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(); } } 

我的解决方案:ID解析由(不幸的)内部类(com.sun.xml.internal.bind.IDResolver)处理,可以从外部设置。

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

解析器可用于解组的许多情况。 但重点是解析器不会在startDocument上清除它自己作为com.sun.xml.internal.bind.v2.runtime.unmarshaller.DefaultIDResolver的默认实现:

 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 { private final Object value; private CallableImplementation(final Object value) { this.value = value; } @Override public Object call() { return value; } } private final Map m = new HashMap(); @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 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); } } }