JAXB / MOXy: не вызывать XmlElementWrapper setter, когда элемент отсутствует? - PullRequest
3 голосов
/ 20 мая 2011

У меня есть установщик списка для класса, который аннотирован как @XmlElementWrapper (name = "foos"), так и @XmlElement (name = "foo").

Когда я отменяю XML, у которого нет илиэлементы, сеттер вызывается и передается пустой список.Есть ли способ получить следующее?:

  • Когда нет, не вызывайте сеттер.Или, если сеттер должен быть вызван, передайте нуль.
  • Когдаприсутствует, но пуст, передать пустой список установщику.
  • Когда имеет одного или нескольких дочерних элементовэлементы, передать заполненный список.

Ответы [ 4 ]

3 голосов
/ 20 мая 2011

Вы можете использовать XmlAdapter для этого варианта использования:

input1.xml

Когда нет, не звоните сеттер. Или если сеттер должен быть Вызван, пропустите ноль.

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

input2.xml

Когда присутствует, но пусто, передать пустой список установщику.

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

input3.xml

Когда есть один или несколько детей стихии, передайте населенный пункт список.

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

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

}

ChildAdapter

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>
1 голос
/ 30 июля 2011

Я пытался решить ту же проблему и довольно долго играл с образцами здесь. Большое спасибо за них, довольно образовательные.

Тем не менее, было неправильно, что что-то настолько зрелое, как реализация JAXB в JDK 6, будет понимать элементы в XML и бездушно вручать мне код пустого списка.

Оказывается, JAXB заполнял список после вызова моего установщика со ссылкой на все еще пустой список, поэтому, когда мой установочный код выглядел так, он пропустил последующие обновления списка во время остаток фазы демаршаллинга:

@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, свидетельство того, что он действительно работал со списком после передачи его в мой код.

1 голос
/ 20 мая 2011

Это адаптер, который в итоге работал на сложности, упомянутые в комментариях к ответу Блеза Дафана:

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

... метод unmarshall не вызывается, если элемент не присутствует вXML.

Я аннотировал свой список свойств следующим образом:

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

public void setFoos(List<Foo> l) {
    ...
}
0 голосов
/ 14 мая 2015

Вы можете поместить всю свою логику в методы beforeUnmarshall, afterUnmarshall или Unmarshaller слушатель, и она будет выполнена, когда демонтаж будет завершен.

См. https://stackoverflow.com/a/4378648/751200 для получения дополнительной информации.

...