3

我在一个用@XmlElementWrapper(name = "foos") 和@XmlElement(name = "foo") 注释的类上有一个列表设置器。

当我解组没有 <foos></foos> 或 <foo/> 元素的 XML 时,将调用 setter 并传递一个空列表。有没有办法获得以下内容?:

  • 当没有 <foos/> 时,不要调用 setter。或者如果必须调用 setter,则传递 null。
  • 当 <foos/> 存在但为空时,将一个空列表传递给 setter。
  • 当 <foos> 有一个或多个子 <foo/> 元素时,传递一个填充列表。
4

4 回答 4

3

对于这个用例,您可以使用 XmlAdapter:

输入1.xml

没有 时,不要调用 setter。或者如果必须调用 setter,则传递 null。

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <child/>
</root>

输入2.xml

当存在但为空时,将一个空列表传递给 setter。

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <child>
        <foos/>
    </child>
</root>

输入3.xml

当有一个或多个子元素时,传递一个填充列表。

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <child>
        <foos>
             <foo>Hello World</foo>
       </foos>
   </child>
</root>

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

@XmlRootElement
public class Root {

    private Child child;

    @XmlJavaTypeAdapter(ChildAdapter.class)
    public Child getChild() {
        return child;
    }

    public void setChild(Child child) {
        this.child = child;
    }

}

孩子

import java.util.List;

public class Child {

    private List<String> strings;

    public List<String> getStrings() {
        return strings;
    }

    public void setStrings(List<String> strings) {
        System.out.println("setStrings");
        this.strings = strings;
    }

}

子适配器

import java.util.ArrayList;
import java.util.List;

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

public class ChildAdapter extends XmlAdapter<ChildAdapter.AdaptedChild, Child> {

    public static class AdaptedChild {
        public Foos foos;
    }

    public static class Foos {
        public List<String> foo;
    }

    @Override
    public Child unmarshal(AdaptedChild adaptedChild) throws Exception {
        Child child = new Child();
        Foos foos = adaptedChild.foos;
        if(null != foos) {
            List<String> foo = foos.foo;
            if(null == foo) {
                child.setStrings(new ArrayList<String>());
            } else {
                child.setStrings(foos.foo);
            }
        }
        return child;
    }

    @Override
    public AdaptedChild marshal(Child child) throws Exception {
        AdaptedChild adaptedChild = new AdaptedChild();
        List<String> strings = child.getStrings();
        if(null != strings) {
            Foos foos = new Foos();
            foos.foo = strings;
            adaptedChild.foos = foos;
        }
        return adaptedChild;
    }

}

演示

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(Root.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        Object o;

        o = unmarshaller.unmarshal(new File("input1.xml"));
        marshaller.marshal(o, System.out);

        o = unmarshaller.unmarshal(new File("input2.xml"));
        marshaller.marshal(o, System.out);

        o = unmarshaller.unmarshal(new File("input3.xml"));
        marshaller.marshal(o, System.out);
    }

}

输出

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <child/>
</root>
setStrings
<?xml version="1.0" encoding="UTF-8"?>
<root>
   <child>
      <foos/>
   </child>
</root>
setStrings
<?xml version="1.0" encoding="UTF-8"?>
<root>
   <child>
      <foos>
         <foo>Hello World</foo>
      </foos>
   </child>
</root>
于 2011-05-20T15:27:28.133 回答
1

这是最终为 Blaise Doughan 的回答评论中提到的并发症工作的适配器:

public class ListOfFooAdapter extends XmlAdapter<ListOfFooAdapter.Adapted, List<Foo>> {
    @XmlRootElement(name = "foos")
    public static class Adapted {
        public List<Foo> foo;
    }

    @Override
    public List<Foo> unmarshal(Adapted adapted) throws Exception {
        return adapted.foo;
    }

    @Override
    public Adapted marshal(List<Foo> foo) throws Exception {
    if (null == foo) {
            return null;
        } else {
            Adapted adapted = new Adapted();
            adapted.foo = foo;
            return adapted;
        }
    }
}

...除非元素存在于 XML 中,否则不会调用 unmarshall 方法。

我像这样注释了我的列表属性:

@XmlJavaTypeAdapter(ListOfFooAdapter.class)
public List<Foo> getFoos() {
    ...
}

public void setFoos(List<Foo> l) {
    ...
}
于 2011-05-20T17:27:01.097 回答
1

我试图解决一个相同的问题,并在这里玩了很长一段时间的示例。非常感谢他们,很有教育意义。

然而,像 JDK 6 中的 JAXB 实现这样成熟的东西能够理解 XML 中的元素并无情地给我编写一个空列表,这似乎并不正确。

事实证明,JAXB 在调用我的 setter 并引用一个仍然为空的列表填充列表,因此当我的 setter 代码看起来像这样时,它在解组阶段的其余部分错过了对列表的后续更新:

@XmlElementWrapper(name = "foos")
@XmlElement(name = "foo")
public void setFoos(List<Foo> newFoos) {
    this.foos.clear();
    this.foos.addAll(newFoos);
}

当我将设置器修改为

@XmlElementWrapper(name = "foos")
@XmlElement(name = "foo")
public void setFoos(List<Foo> newFoos) {
    this.foos = newFoos;
}

它工作得很好。由于列表所有者可以在事后对其进行修改,因此一直依赖其他人指向列表的指针总是一种紧张的体验,但在这种情况下,正是这种改变解决了问题。

我通过遍历 JAXB 传递的列表确认了我的假设,果然它抱怨了 ConcurrentModificationException,证明它确实在将它交给我的代码后在列表上工作。

于 2011-07-30T05:54:24.370 回答
0

您可以将所有逻辑放在beforeUnmarshallafterUnmarshall方法或Unmarshaller侦听器中,它将在解组完成时执行。

有关更多信息,请参阅https://stackoverflow.com/a/4378648/751200

于 2015-05-14T10:09:08.727 回答