xml - JAXB 是否可以首先通过包含进行编码(marshal),然后通过 @XmlIDREF 进行编码(marshal)以供后续引用?

标签 xml jaxb marshalling jaxb2 marshalbyrefobject

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

最佳答案

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

input.xml

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

<?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 的同一实例可能会在集合中出现多次。

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 来处理。 XmlAdapter 使用 @XmlJavaTypeAdapter 注释进行配置。由于我们已在类型/类级别指定了此适配器,因此它将应用于引用 PhoneNumber 类的所有属性:

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 的子类。

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());
    }

}

电话号码适配器

下面是XmlAdapter的实现。请注意,我们必须维护 PhoneNumber 对象之前是否已出现过。如果有,我们只填充 AdaptedPhoneNumber 对象的 id 部分。

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;
    }

}

演示

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

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>

了解更多信息

关于xml - JAXB 是否可以首先通过包含进行编码(marshal),然后通过 @XmlIDREF 进行编码(marshal)以供后续引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20670755/

相关文章:

java - XML 解码期间的 NullPointerException

ocaml - 使用 OCaml 编码数据通信客户端 - 服务器

c# - 如何将对象数组传递给非托管代码?

android - Android XML预览- “The following classes could not be found”

c# - 将 xml 数据规范化为对象 linq to xml

java - 带有 MTOM 附件的 Spring-WS web 服务 - Hello World 测试

c# - 没有装箱的 Marshal.StructureToPtr

java - 机器人:大写不工作

java - 如何在java中动态更改xml?

jaxb - Karaf 的 Moxy 错误