简体   繁体   English

moxy jaxb中的XmlAdapter和XmlIDREF

[英]XmlAdapter and XmlIDREF in moxy jaxb

I am trying to use MOXy JAXB to serialize a class A which looks like: 我正在尝试使用MOXy JAXB来序列化类A,它看起来像:

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement
public class A {
    private Map<Foo, Bar> fooBar = new HashMap<Foo, Bar>();
    private Set<Foo> foos = new HashSet<Foo>();

    @XmlJavaTypeAdapter(FooBarMapAdapter.class)
    public Map<Foo, Bar> getFooBar() {
        return fooBar;
    }

    public void setFooBar(Map<Foo, Bar> fooBar) {
        this.fooBar = fooBar;
    }

    @XmlElement
    public Set<Foo> getFoos() {
        return foos;
    }

    public void setFoos(Set<Foo> foos) {
        this.foos = foos;
    }
}

The point is that the Foo objects in the "foos" fields are a superset of those in the fooBar map. 关键是“foos”字段中的Foo对象是fooBar映射中的Foo对象的超集。 Therefore I would like to "link" the key elements for the "fooBar" map to the corresponding elements in the "foos" list. 因此,我想将“fooBar”映射的关键元素“链接”到“foos”列表中的相应元素。 I have tried this using the XmlID and XmlIDREF annotations: 我使用XmlID和XmlIDREF注释尝试了这个:

@XmlAccessorType(XmlAccessType.NONE)
public class Foo {
    private String xmlId;

    @XmlID
    @XmlAttribute
    public String getXmlId() {
        return xmlId;
    }

    public void setXmlId(String xmlId) {
        this.xmlId = xmlId;
    }
}


@XmlAccessorType(XmlAccessType.NONE)
public class Bar {
    // Some code...
}

Then in my XmlAdapter I have tried to use a XmlIDREF annotation on the adapted map entries' foo object: 然后在我的XmlAdapter中,我尝试在改编后的映射条目的foo对象上使用XmlIDREF注释:

public class FooBarMapAdapter extends
        XmlAdapter<FooBarMapAdapter.FooBarMapType, Map<Foo, Bar>> {
    public static class FooBarMapType {
        public List<FooBarMapEntry> entries = new ArrayList<FooBarMapEntry>();
    }

    @XmlAccessorType(XmlAccessType.NONE)
    public static class FooBarMapEntry {
        private Foo foo;
        private Bar bar;

        @XmlIDREF
        @XmlAttribute
        public Foo getFoo() {
            return foo;
        }

        public void setFoo(Foo foo) {
            this.foo = foo;
        }

        @XmlElement
        public Bar getBar() {
            return bar;
        }

        public void setBar(Bar bar) {
            this.bar = bar;
        }
    }

    @Override
    public FooBarMapType marshal(Map<Foo, Bar> map) throws Exception {
        FooBarMapType fbmt = new FooBarMapType();
        for (Map.Entry<Foo, Bar> e : map.entrySet()) {
            FooBarMapEntry entry = new FooBarMapEntry();
            entry.setFoo(e.getKey());
            entry.setBar(e.getValue());
            fbmt.entries.add(entry);
        }
        return fbmt;
    }

    @Override
    public Map<Foo, Bar> unmarshal(FooBarMapType fbmt) throws Exception {
        Map<Foo, Bar> map = new HashMap<Foo, Bar>();
        for (FooBarMapEntry entry : fbmt.entries) {
            map.put(entry.getFoo(), entry.getBar());
        }
        return map;
    }
}

When marshaling the code above is working as expected and produces the following XML: 编组上面的代码时按预期工作并生成以下XML:

<?xml version="1.0" encoding="UTF-8"?>
<a>
   <fooBar>
      <entries foo="nr1">
         <bar/>
      </entries>
   </fooBar>
   <foos xmlId="nr1"/>
</a>

For testing unmarshal, I am using the following test-code: 为了测试unmarshal,我使用以下测试代码:

public class Test {
    public static void main(String[] args) throws Exception {
        A a = new A();

        Map<Foo, Bar> map = new HashMap<Foo, Bar>();
        Foo foo = new Foo();
        foo.setXmlId("nr1");
        Bar bar = new Bar();
        map.put(foo, bar);
        a.setFooBar(map);
        a.setFoos(map.keySet());

        final File file = new File("test.xml");
        if (!file.exists())
            file.createNewFile();
        FileOutputStream fos = new FileOutputStream(file);

        JAXBContext jc = JAXBContext.newInstance(A.class);
        Marshaller m = jc.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        m.marshal(a, fos);

        FileInputStream fis = new FileInputStream(file);
        Unmarshaller um = jc.createUnmarshaller();
        A newA = (A) um.unmarshal(fis);

        System.out.println(newA.getFooBar());
    }
}

This code produces the (for me) unexpected result: 此代码生成(对我来说)意外结果:

{null=test.moxy.Bar@373c0b53}

That is, the Foo object used as key in the map is null. 也就是说,在地图中用作键的Foo对象为null。 If I change the map adapter and marshal the Foo object twice, instead of using an ID reference, I do not get this null pointer. 如果我更改地图适配器并封送Foo对象两次,而不是使用ID引用,我不会得到这个空指针。

I have been able to find some posts about this on google using the JAXB-RI, where the problem could be solved writing an IDResolver as described at http://weblogs.java.net/blog/2005/08/15/pluggable-ididref-handling-jaxb-20 . 我已经能够在谷歌上使用JAXB-RI找到一些关于这个的帖子,在这里可以解决问题写一个IDResolver,如http://weblogs.java.net/blog/2005/08/15/pluggable-所述 - ididref-handling-jaxb-20 Unfortunately I have not been able to find any information about such a class in the MOXy JAXB JavaDoc. 不幸的是,我无法在MOXy JAXB JavaDoc中找到有关此类的任何信息。

Suggestion for workaround From Blaise Doughan answer, I have realized that this is a bug in the MOXy implementation of JAXB. 对解决方法的建议从Blaise Doughan的回答中,我意识到这是JAXB的MOXy实现中的一个错误。 I have been able to make a (ugly) workaround for this bug. 我已经能够为这个bug制作一个(丑陋的)解决方法。 The idea is that instead of using a XMLAdapter the map is "converted" inside its defining class. 我们的想法是,不是使用XMLAdapter,而是在其定义类中“转换”地图。 The class A now looks like: A类现在看起来像:

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement
public class A {
    private Map<Foo, Bar> fooBar = new HashMap<Foo, Bar>();
    private Set<Foo> foos = new HashSet<Foo>();

    // Due to a bug a XMLAdapter approch is not possible when using XmlIDREF.
    // The map is mapped by the wrapper method getXmlableFooBarMap.
    // @XmlJavaTypeAdapter(FooBarMapAdapter.class)
    public Map<Foo, Bar> getFooBar() {
        return fooBar;
    }

    public void setFooBar(Map<Foo, Bar> fooBar) {
        this.fooBar = fooBar;
    }

    @XmlElement
    public Set<Foo> getFoos() {
        return foos;
    }

    public void setFoos(Set<Foo> foos) {
        this.foos = foos;
    }

    // // WORKAROUND FOR JAXB BUG /////
    private List<FooBarMapEntry> mapEntries;

    @XmlElement(name = "entry")
    public List<FooBarMapEntry> getXmlableFooBarMap() {
        this.mapEntries = new LinkedList<FooBarMapEntry>();
        if (getFooBar() == null)
            return mapEntries;

        for (Map.Entry<Foo, Bar> e : getFooBar().entrySet()) {
            FooBarMapEntry entry = new FooBarMapEntry();
            entry.setFoo(e.getKey());
            entry.setBar(e.getValue());
            mapEntries.add(entry);
        }

        return mapEntries;
    }

    public void setXmlableFooBarMap(List<FooBarMapEntry> entries) {
        this.mapEntries = entries;
    }

    public void transferFromListToMap() {
        fooBar = new HashMap<Foo, Bar>();
        for (FooBarMapEntry entry : mapEntries) {
            fooBar.put(entry.getFoo(), entry.getBar());
        }
    }
}

After the unmarshal, the transferFromListToMap-method now needs to be called. 在unmarshal之后,现在需要调用transferFromListToMap方法。 So the following line should be added immediately after the reference to newA is obtained: 因此,在获得对newA的引用后,应立即添加以下行:

newA.transferFromListToMap();

Any suggestions for a nicer workaround / bug fix will be appreciated :). 任何有关更好的解决方法/错误修复的建议将不胜感激:)。

Note: I'm the EclipseLink JAXB (MOXy) lead. 注意:我是EclipseLink JAXB(MOXy)的负责人。

I have been able to confirm the issue that you are seeing: 我已经能够确认您所看到的问题:

Why the Issue is Happening 为什么问题正在发生

The issue is due to MOXy processing the XmlAdapter logic before it has processed the @XmlIDREF logic. 问题是由于MOXy在处理XmlAdapter逻辑之前处理@XmlIDREF逻辑。 MOXy does a single pass of the XML document, and the @XmlIDREF relationships are processed at the end to ensure that all of the referenced objects have been built (as the reference may precede the referenced object, as in this case). MOXy执行XML文档的单次传递,并在@XmlIDREF处理@XmlIDREF关系以确保已构建所有引用的对象(因为引用可能在引用的对象之前,如本例所示)。

I will try to post a workaround to this issue, and you can track our progress on this issue using the above bug. 我将尝试针对此问题发布变通方法,您可以使用上述错误跟踪我们在此问题上的进展。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM