EclipseLink MOXy具有相同类型的递归数据结构/子元素

时间:2012-08-02 01:16:33

标签: java jaxb eclipselink moxy

我正在使用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>

感谢。

1 个答案:

答案 0 :(得分:3)

要完成此用例,我们将利用JAXB中的两个概念:XmlAdapterMarshaller.Listener

<强> MyClassAdapter

我们将利用默认的JAXB行为,而不是为空值编组元素。为此,我们将实现一个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.Listener创建的实例Marshaller

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>

了解更多信息

以下文章对本答案中讨论的主题进行了扩展: