我使用的是EclipseLink MOXy,并且有一个具有相同数据类型的子元素的数据结构。现在我不想序列化具有无限深度的数据结构,而只想序列化第一级。
以下是数据结构的一些示例代码:
package test;
import java.util.Collection;
import java.util.Vector;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlRootElement
public class MyClass {
private int id;
private String details;
private Collection<MyClass> children = new Vector<MyClass>();
public MyClass() {
}
public MyClass(int id, String details) {
this.id = id;
this.details = details;
}
@XmlElementWrapper
@XmlElementRef
public Collection<MyClass> getChildren() {
return children;
}
public void addChild(MyClass child) {
children.add(child);
}
public String getDetails() {
return details;
}
@XmlAttribute
public int getId() {
return id;
}
public void setChildren(Collection<MyClass> children) {
this.children = children;
}
public void setDetails(String details) {
this.details = details;
}
public void setId(int id) {
this.id = id;
}
}
我的测试程序:
package test;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Test {
public static void main(String[] args) throws Exception {
MyClass l1 = new MyClass(1, "Level 1");
MyClass l2 = new MyClass(2, "Level 2");
l1.addChild(l2);
MyClass l3 = new MyClass(3, "Level 3");
l2.addChild(l3);
JAXBContext jc = JAXBContext.newInstance(MyClass.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(l1, System.out);
}
}
生成以下XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myClass id="1">
<children>
<myClass id="2">
<children>
<myClass id="3">
<children/>
<details>Level 3</details>
</myClass>
</children>
<details>Level 2</details>
</myClass>
</children>
<details>Level 1</details>
</myClass>
然而,我也希望xml看起来像:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myClass id="1">
<children>
<myClass id="2">
<details>Level 2</details>
</myClass>
</children>
<details>Level 1</details>
</myClass>
谢谢。
为了完成这个用例,我们将利用JAXB的两个概念:XmlAdapter
和Marshaller.Listener.
MyClassAdapter
我们将利用默认的JAXB行为,即不为null值编组元素。为此,我们将实现一个XmlAdapter
,它在达到指定级别后返回null。为了计算级别,我们将创建一个Marshaller.Listener
。
package forum11769758;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class MyClassAdapter extends XmlAdapter<MyClass, MyClass>{
private int levels;
private MyMarshallerListener marshallerListener;
public MyClassAdapter() {
}
public MyClassAdapter(int levels) {
this.levels = levels;
}
public Marshaller.Listener getMarshallerListener() {
if(null == marshallerListener) {
marshallerListener = new MyMarshallerListener();
}
return marshallerListener;
}
@Override
public MyClass marshal(MyClass myClass) throws Exception {
if(null == marshallerListener || marshallerListener.getLevel() < levels) {
return myClass;
}
return null;
}
@Override
public MyClass unmarshal(MyClass myClass) throws Exception {
return myClass;
}
static class MyMarshallerListener extends Marshaller.Listener {
private int level = 0;
public int getLevel() {
return level;
}
@Override
public void afterMarshal(Object object) {
if(object instanceof MyClass) {
level--;
}
}
@Override
public void beforeMarshal(Object object) {
if(object instanceof MyClass) {
level++;
}
}
}
}
MyClass
@XmlJavaTypeAdapter
注释用于指定应使用XmlAdapter
。
package forum11769758;
import java.util.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlRootElement
public class MyClass {
private int id;
private String details;
private Collection<MyClass> children = new Vector<MyClass>();
public MyClass() {
}
public MyClass(int id, String details) {
this.id = id;
this.details = details;
}
@XmlElementWrapper
@XmlElementRef
@XmlJavaTypeAdapter(MyClassAdapter.class)
public Collection<MyClass> getChildren() {
return children;
}
public void addChild(MyClass child) {
children.add(child);
}
public String getDetails() {
return details;
}
@XmlAttribute
public int getId() {
return id;
}
public void setChildren(Collection<MyClass> children) {
this.children = children;
}
public void setDetails(String details) {
this.details = details;
}
public void setId(int id) {
this.id = id;
}
}
测试
由于我们需要以有状态的方式使用XmlAdapter
,我们将在Marshaller
上设置它的一个实例,我们还将设置我们在Marshaller
上创建的Marshaller.Listener
的实例。
package forum11769758;
import javax.xml.bind.*;
public class Test {
public static void main(String[] args) throws Exception {
MyClass l1 = new MyClass(1, "Level 1");
MyClass l2 = new MyClass(2, "Level 2");
l1.addChild(l2);
MyClass l3 = new MyClass(3, "Level 3");
l2.addChild(l3);
JAXBContext jc = JAXBContext.newInstance(MyClass.class);
Marshaller marshaller = jc.createMarshaller();
MyClassAdapter myClassAdapter = new MyClassAdapter(2);
marshaller.setAdapter(myClassAdapter);
marshaller.setListener(myClassAdapter.getMarshallerListener());
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(l1, System.out);
}
}
输出
<?xml version="1.0" encoding="UTF-8"?>
<myClass id="1">
<children>
<myClass id="2">
<children/>
<details>Level 2</details>
</myClass>
</children>
<details>Level 1</details>
</myClass>
有关更多信息
以下文章对本答案中讨论的主题进行了扩展:
- http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html
- http://blog.bdoughan.com/2011/09/mixing-nesting-and-references-with.html