如何使用 Java XML 绑定将对象图编组到特定深度?

发布于 2024-11-06 23:26:03 字数 243 浏览 4 评论 0原文

作为 Java XML 绑定的新手,我面临着一个挑战。

假设我有一个场景,其中构建了我的域模型,并且我想将此域编组为 xml 结构。

现在我想提供不同的解组路径:

  1. 编组整个对象图[这里没有问题]
  2. 编组对象图直到特定深度! [挑战]

我无法找到一个在不引入太多复杂性的情况下解决这个问题的好方法。人们可以复制域并稍后手动复制,但这感觉不对。还有其他可用的解决方案吗?

being a newbie to Java XML binding i am facing a challenge.

Let say i have a scenario where my domain model is constructed and i want to marshall this domain to an xml structure.

Now i want to provide different unmarshall path's:

  1. Marshall the whole object graph [no problem here]
  2. Marshall an objectgraph until a specific depth!!! [challenge]

I cannot figure out a good way on how to tackle this without introducing to much complexity. One can make a copy of the domain and manually later that, but that does not feel right. Any other solutions available?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

[浮城] 2024-11-13 23:26:03

您可以利用 XmlAdapter 和 Marshal.Listener 来获得此行为:

演示

将设置 Marshal.Listener 来跟踪我们正在编组的树的深度。此外,我们还将设置可识别深度侦听器的运行时级别 XmlAdapter。当达到所需深度时,这些适配器将开始返回 null。

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);

        Root rootA = new Root();
        rootA.setName("A");

        Root rootB = new Root();
        rootB.setName("B");
        rootA.setChild(rootB);

        Root rootC = new Root();
        rootC.setName("C");
        rootB.setChild(rootC);

        Root rootD = new Root();
        rootD.setName("D");
        rootC.setChild(rootD);

        Root rootE = new Root();
        rootE.setName("E");
        rootD.setChild(rootE);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        DepthListener depthListener = new DepthListener(3);
        marshaller.setListener(depthListener);
        marshaller.setAdapter(new RootAdapter(depthListener));
        marshaller.marshal(rootA, System.out);
    }

}

DepthListener

这个类的目的是跟踪当前的深度。

import javax.xml.bind.Marshaller;

public class DepthListener extends Marshaller.Listener {

    private int targetDepth;
    private int currentDepth = 0;

    public DepthListener(int depth) {
        this.targetDepth = depth;
    }

    @Override
    public void beforeMarshal(Object source) {
        currentDepth++;
    }

    @Override
    public void afterMarshal(Object source) {
        currentDepth--;
    }

    public boolean isMarshalDepth() {
        return currentDepth <= targetDepth; 
    }

}

RootAdapter

XmlAdapter 的用途是在达到所需深度时开始返回 null 以停止编组过程。

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

public class RootAdapter extends XmlAdapter<Root, Root> {

    private DepthListener depthListener;

    public RootAdapter() {
    }

    public RootAdapter(DepthListener depthListener) {
        this.depthListener = depthListener;
    }

    @Override
    public Root unmarshal(Root root) throws Exception {
        return root;
    }

    @Override
    public Root marshal(Root root) throws Exception {
        if(depthListener != null && !depthListener.isMarshalDepth()) {
            return null;
        }
        return root;
    }

}

Root

下面演示了如何通过 @XmlJavaTypeAdapter 注释在域对象上指定 XmlAdapter:

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

@XmlRootElement
@XmlJavaTypeAdapter(RootAdapter.class)
@XmlType(propOrder={"name", "child"})
public class Root {

    private String name;
    private Root child;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Root getChild() {
        return child;
    }

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

}

输出

以下是演示代码的输出:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <name>A</name>
    <child>
        <name>B</name>
        <child>
            <name>C</name>
        </child>
    </child>
</root>

You could leverage XmlAdapter and Marshal.Listener to get this behaviour:

Demo

A Marshal.Listener will be set to keep track of the depth of the tree we are marshalling. Also we will set runtime level XmlAdapters that are aware of the depth listener. These adapters will start returning null when the desired depth has been reached.

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);

        Root rootA = new Root();
        rootA.setName("A");

        Root rootB = new Root();
        rootB.setName("B");
        rootA.setChild(rootB);

        Root rootC = new Root();
        rootC.setName("C");
        rootB.setChild(rootC);

        Root rootD = new Root();
        rootD.setName("D");
        rootC.setChild(rootD);

        Root rootE = new Root();
        rootE.setName("E");
        rootD.setChild(rootE);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        DepthListener depthListener = new DepthListener(3);
        marshaller.setListener(depthListener);
        marshaller.setAdapter(new RootAdapter(depthListener));
        marshaller.marshal(rootA, System.out);
    }

}

DepthListener

The purpose of this class is to to keep track of the current depth.

import javax.xml.bind.Marshaller;

public class DepthListener extends Marshaller.Listener {

    private int targetDepth;
    private int currentDepth = 0;

    public DepthListener(int depth) {
        this.targetDepth = depth;
    }

    @Override
    public void beforeMarshal(Object source) {
        currentDepth++;
    }

    @Override
    public void afterMarshal(Object source) {
        currentDepth--;
    }

    public boolean isMarshalDepth() {
        return currentDepth <= targetDepth; 
    }

}

RootAdapter

The purpose of the XmlAdapter is to start returning null when the desired depth has been reached to stop the marshalling process.

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

public class RootAdapter extends XmlAdapter<Root, Root> {

    private DepthListener depthListener;

    public RootAdapter() {
    }

    public RootAdapter(DepthListener depthListener) {
        this.depthListener = depthListener;
    }

    @Override
    public Root unmarshal(Root root) throws Exception {
        return root;
    }

    @Override
    public Root marshal(Root root) throws Exception {
        if(depthListener != null && !depthListener.isMarshalDepth()) {
            return null;
        }
        return root;
    }

}

Root

The following demonstrates how to specify the XmlAdapter on the domain object via the @XmlJavaTypeAdapter annotation:

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

@XmlRootElement
@XmlJavaTypeAdapter(RootAdapter.class)
@XmlType(propOrder={"name", "child"})
public class Root {

    private String name;
    private Root child;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Root getChild() {
        return child;
    }

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

}

Output

The following is the output from the demo code:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <name>A</name>
    <child>
        <name>B</name>
        <child>
            <name>C</name>
        </child>
    </child>
</root>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文