JAXB 可以先通过遏制封送,然后通过@XmlIDREF 封送以供后续参考吗? [英] Can JAXB marshal by containment at first then marshal by @XmlIDREF for subsequent references?

查看:30
本文介绍了JAXB 可以先通过遏制封送,然后通过@XmlIDREF 封送以供后续参考吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道是否可以注释我的类,以便编组器第一次遇到一个对象时,它会生成一个适当类型的 XML 元素,但是任何其他对该对象的后续引用都将具有 XML IDREF条目创建了吗?

I'm wondering if it's possible to annotate my classes so that the first time the marshaller encounters an object, it generates an XML element of the appropriate type, but any subsequent reference to this object by anything else will have an XML IDREF entry created?

推荐答案

您可以利用 JAXB 的 XmlAdapter 的概念来执行以下操作:

You can leverage the concept of JAXB's XmlAdapter to do something like the following:

input.xml

以下是我将用于此示例的 XML 文档.第 3 个 phone-number 条目是对第一个 phone-number 条目的引用,而第 5 个 phone-number 条目是对第四:

The following is the XML document I will use for this example. The 3rd phone-number entry is a reference to the 1st phone-number entry, and the 5th phone-number entry is a reference to the 4th.:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
    <phone-number id="A">
        <number>555-AAAA</number>
    </phone-number>
    <phone-number id="B">
        <number>555-BBBB</number>
    </phone-number>
    <phone-number id="A"/>
    <phone-number xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="work-phone-number" id="W">
        <number>555-WORK</number>
        <extension>1234</extension>
    </phone-number>
    <phone-number xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="work-phone-number" id="W"/>
</customer>

客户

客户类维护PhoneNumber 对象的集合.同一个 PhoneNumber 实例可能会在集合中出现多次.

The customer class maintains a collection of PhoneNumber objects. The same instance of PhoneNumber may appear multiple times in the collection.

package forum7587095;

import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer {

    private List<PhoneNumber> phoneNumbers;

    @XmlElement(name="phone-number")
    public List<PhoneNumber> getPhoneNumbers() {
        return phoneNumbers;
    }

    public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
        this.phoneNumbers = phoneNumbers;
    }

}

电话号码

这是一个可以出现在文档本身或作为参考的类.这将使用 XmlAdapter 进行处理.使用 @XmlJavaTypeAdapter 注释配置 XmlAdapter.由于我们在类型/类级别指定了此适配器,因此它将应用于引用 PhoneNumber 类的所有属性:

This is a class that can either appear in the document itself or as a reference. This will be handled using an XmlAdapter. An XmlAdapter is configured using the @XmlJavaTypeAdapter annotation. Since we have specified this adapter at the type/class level it will apply to all properties referencing the PhoneNumber class:

package forum7587095;

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

@XmlJavaTypeAdapter(PhoneNumberAdapter.class)
public class PhoneNumber {

    private String id;
    private String number;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    @Override
    public boolean equals(Object arg0) {
        if(null == arg0 || arg0.getClass() != this.getClass()) {
            return false;
        }
        PhoneNumber test = (PhoneNumber) arg0;
        if(!equals(id, test.getId())) {
            return false;
        }
        return equals(number, test.getNumber());
    }

    protected boolean equals(String control, String test) {
        if(null == control) {
            return null == test;
        } else {
            return control.equals(test);
        }
    }

    @Override
    public int hashCode() {
        return id.hashCode();
    }

}

工作电话号码

根据您的评论,我添加了 PhoneNumber 的子类.

Based on your comment I have added a subclass of PhoneNumber.

package forum7587095;

public class WorkPhoneNumber extends PhoneNumber {

    private String extension;

    public String getExtension() {
        return extension;
    }

    public void setExtension(String extension) {
        this.extension = extension;
    }

    @Override
    public boolean equals(Object arg0) {
        if(!super.equals(arg0)) {
            return false;
        }
        return equals(extension, ((WorkPhoneNumber) arg0).getExtension());
    }

}

PhoneNumberAdapter

下面是XmlAdapter的实现.请注意,如果之前已经看到过 PhoneNumber 对象,我们必须进行维护.如果有,我们只填充 AdaptedPhoneNumber 对象的 id 部分.

Below is the implementation of the XmlAdapter. Note that we must maintain if the PhoneNumber object has been seen before. If it has we only populate the id portion of the AdaptedPhoneNumber object.

package forum7587095;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class PhoneNumberAdapter extends XmlAdapter<PhoneNumberAdapter.AdaptedPhoneNumber, PhoneNumber>{

    private List<PhoneNumber> phoneNumberList = new ArrayList<PhoneNumber>();
    private Map<String, PhoneNumber> phoneNumberMap = new HashMap<String, PhoneNumber>();

    @XmlSeeAlso(AdaptedWorkPhoneNumber.class)
    @XmlType(name="phone-number")
    public static class AdaptedPhoneNumber {
        @XmlAttribute public String id;
        public String number;

        public AdaptedPhoneNumber() {
        }

        public AdaptedPhoneNumber(PhoneNumber phoneNumber) {
            id = phoneNumber.getId();
            number = phoneNumber.getNumber();
        }

        public PhoneNumber getPhoneNumber() {
            PhoneNumber phoneNumber = new PhoneNumber();
            phoneNumber.setId(id);
            phoneNumber.setNumber(number);
            return phoneNumber;
        }

    }

    @XmlType(name="work-phone-number")
    public static class AdaptedWorkPhoneNumber extends AdaptedPhoneNumber {

        public String extension;

        public AdaptedWorkPhoneNumber() {
        }

        public AdaptedWorkPhoneNumber(WorkPhoneNumber workPhoneNumber) {
            super(workPhoneNumber);
            extension = workPhoneNumber.getExtension();
        }

        @Override
        public WorkPhoneNumber getPhoneNumber() {
            WorkPhoneNumber phoneNumber = new WorkPhoneNumber();
            phoneNumber.setId(id);
            phoneNumber.setNumber(number);
            phoneNumber.setExtension(extension);
            return phoneNumber;
        }
}

    @Override
    public AdaptedPhoneNumber marshal(PhoneNumber phoneNumber) throws Exception {
        AdaptedPhoneNumber adaptedPhoneNumber;
        if(phoneNumberList.contains(phoneNumber)) {
            if(phoneNumber instanceof WorkPhoneNumber) {
                adaptedPhoneNumber = new AdaptedWorkPhoneNumber();
            } else {
                adaptedPhoneNumber = new AdaptedPhoneNumber();
            }
            adaptedPhoneNumber.id = phoneNumber.getId();
        } else {
            if(phoneNumber instanceof WorkPhoneNumber) {
                adaptedPhoneNumber = new AdaptedWorkPhoneNumber((WorkPhoneNumber)phoneNumber);
            } else {
                adaptedPhoneNumber = new AdaptedPhoneNumber(phoneNumber);
            }
            phoneNumberList.add(phoneNumber);
        }
        return adaptedPhoneNumber;
    }

    @Override
    public PhoneNumber unmarshal(AdaptedPhoneNumber adaptedPhoneNumber) throws Exception {
        PhoneNumber phoneNumber = phoneNumberMap.get(adaptedPhoneNumber.id);
        if(null != phoneNumber) {
            return phoneNumber;
        }
        phoneNumber = adaptedPhoneNumber.getPhoneNumber();
        phoneNumberMap.put(phoneNumber.getId(), phoneNumber);
        return phoneNumber;
    }

}

演示

为了确保 XmlAdapter 的相同实例用于整个 marshalunmarshal 操作,我们必须专门设置 XmlAdapter 的实例在 MarshallerUnmarshaller 上:

To ensure the same instance of the XmlAdapter is used for the entire marshal and unmarshal operations we must specifically set an instance of the XmlAdapter on both the Marshaller and Unmarshaller:

package forum7587095;

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(Customer.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setAdapter(new PhoneNumberAdapter());
        File xml = new File("src/forum7587095/input.xml");
        Customer customer = (Customer) unmarshaller.unmarshal(xml);

        System.out.println(customer.getPhoneNumbers().get(0) == customer.getPhoneNumbers().get(2));
        System.out.println(customer.getPhoneNumbers().get(3) == customer.getPhoneNumbers().get(4));

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setAdapter(new PhoneNumberAdapter());
        marshaller.marshal(customer, System.out);
    }

}

输出

true
true
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
    <phone-number id="A">
        <number>555-AAAA</number>
    </phone-number>
    <phone-number id="B">
        <number>555-BBBB</number>
    </phone-number>
    <phone-number id="A"/>
    <phone-number xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="work-phone-number" id="W">
        <number>555-WORK</number>
        <extension>1234</extension>
    </phone-number>
    <phone-number xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="work-phone-number" id="W"/>
</customer>

更多信息

这篇关于JAXB 可以先通过遏制封送,然后通过@XmlIDREF 封送以供后续参考吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆