为了账号安全,请及时绑定邮箱和手机立即绑定

使用JAXB交叉引用来自两个XML文件的XmlID

使用JAXB交叉引用来自两个XML文件的XmlID

智慧大石 2019-12-05 15:38:19
我正在尝试将两个不同的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类以指向其引用的正确Node。但是,我还必须解析此XML:--NetworkInputs.xml--<NetworkInputs>  <Flows>    <Flow toNode="JND" />    <Flow toNode="ROD" />  </Flows></NetworkInputs>------当前,我的程序成功地解组了Network对象,但是Network和NetworkInputs之间没有连接,该连接允许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 ,但它不起作用,因为我无法从中获取填充网络的Node数据。静态上下文。甚至可以做这样的事情吗?
查看完整描述

2 回答

?
暮色呼如

TA贡献1853条经验 获得超9个赞

可以使用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传递给解组器:


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

    }


}


查看完整回答
反对 回复 2019-12-05
?
慕无忌1623718

TA贡献1744条经验 获得超4个赞

我的解决方案: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);

        }

    }

}


查看完整回答
反对 回复 2019-12-05
  • 2 回答
  • 0 关注
  • 563 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信