【问题标题】:Using JAXB to cross reference XmlIDs from two XML files使用 JAXB 从两个 XML 文件交叉引用 XmlID
【发布时间】:2023-03-26 08:15:02
【问题描述】:

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

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

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

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

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

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

我基本上想这样做: 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 它只是不起作用,因为我无法从静态上下文中获取填充网络的节点数据。

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

【问题讨论】:

    标签: java xml jaxb


    【解决方案1】:

    这可以通过 XmlAdapter 来完成。诀窍是 XmlAdapter 需要使用 Network.xml 中的所有节点进行初始化,并传递给与 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<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();
        }
    
    }
    

    【讨论】:

    • 好主意,你应该把它贴在你的博客上。
    【解决方案2】:

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

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

    解析器可用于解组器的多个实例。 但关键是解析器不会像 com.sun.xml.internal.bind.v2.runtime.unmarshaller.DefaultIDResolver 的默认实现那样在 startDocument 上清除自己:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-15
      • 1970-01-01
      • 2017-08-21
      • 1970-01-01
      • 2016-03-13
      • 1970-01-01
      相关资源
      最近更新 更多