How to unmarshall repeated nested classes with JAX

2019-02-06 10:04发布

问题:

How can I instruct JAXB to process this ?

XML

<root>
 <parent>
    <child id="1" name="foo" />
 </parent>
 <parent>
    <child id="3" name="foo2" />
 </parent>
 <parent>
    <child id="4" name="bar2" />
 </parent>
 <parent>
    <child id="2" name="bar" />
 </parent>
</root>

Root.java

@XmlRootElement
public class Root {
   @XmlElement(name="parent/child")
   List<Child> allChildren;
}

This doesn't work ... allChildren is empty.

回答1:

You could change your model and do the following:

Root

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {
   @XmlElement(name="parent")
   List<Parent> allParents;
}

Parent

@XmlAccessorType(XmlAccessType.FIELD)
public class Parent {
   @XmlElement(name="child")
   List<Child> allChildren;
}

UPDATE

Is it possible to avoid the parent class ?

There are a couple of different ways to accomplish this:

OPTION #1 - Any JAXB Implementation using XmlAdapter

You could use an XmlAdapter to virtually add in the Parent class.

ChildAdapter

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

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

    public static class Parent {
        public Child child;
    }

    @Override
    public Parent marshal(Child v) throws Exception {
        Parent parent = new Parent();
        parent.child = v;
        return parent;
    }

    @Override
    public Child unmarshal(Parent v) throws Exception {
        return v.child;
    }

}

Root

The @XmlJavaTypeAdapter annotation is used to reference the XmlAdapter.

import java.util.List;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {

   @XmlElement(name="parent")
   @XmlJavaTypeAdapter(ChildAdapter.class)
   List<Child> allChildren;

}

Child

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Child {

    @XmlAttribute
    int id;

    @XmlAttribute
    String name;

}

OPTION #2 - Using EclipseLink JAXB (MOXy)

If you are using EclipseLink JAXB (MOXy) as your JAXB (JSR-222) implementation then you could do the following (Note: I'm the MOXy lead):

Root

import java.util.List;
import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {

   @XmlElement(name="parent")
   List<Child> allChildren;

}

Child

MOXy's @XmlPath annotation works pretty much the way you are trying to use the @XmlElement annotation in your post.

import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlAccessorType(XmlAccessType.FIELD)
public class Child {

    @XmlPath("child/@id")
    int id;

    @XmlPath("child/@name")
    String name;

}

For More Information

  • http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
  • http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html


回答2:

You will have to create a class representing the <parent> element, such as

@XmlAccessorType(XmlAccessType.FIELD)
public class Parent {
   @XmlElement(name="child")
   Child child;
}

You could then create a type adapter

public class ParentToChildAdapter extends XmlAdapter<Parent, Child> {
  public Parent marshal(Child c) {
    Parent p = new Parent();
    p.child = child;
    return p;
  }

  public Child unmarshal(Parent p) {
    return p.child;
  }
}

and use this on the root class

@XmlRootElement
public class Root {
   @XmlElement(name="parent")
   @XmlJavaTypeAdapter(ParentToChildAdapter.class)
   List<Child> allChildren;
}


回答3:

You could try to use the XmlElementWrapper annotation but I am not sure how it should work with multiple wrapper nodes:

@XmlRootElement
public class Root {
   @XmlElementWrapper(name="parent")
   @XmlElement(name="child")
   List<Child> allChildren;
}


回答4:

Try this

@XmlRootElement
class Root {
    List<Child> allChildren = new ArrayList<Child>();

    private static class Parent  {
        @XmlElement
        Child child;
    }

    @XmlElement
    public void setParent(Parent p) {
        allChildren.add(p.child);
    }
}


标签: java jaxb