@XmlRegistry - 它是如何工作的?

11
我在互联网上找到了一些JAXB2 @XmlRegistry的例子,但没有很好深入的教程,讲解如何使用@XmlRegistry@XmlElementDecl的概念,不知道这个概念是否广泛探讨过。
无论如何,这是我的问题,首先是我正在使用JAXB解组XML时使用的一些示例类:
我正在尝试使用JAXB解组的主要类 - Employee.java。
package com.test.jaxb;

import java.util.List;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;

import com.test.jaxb.dto.Address;

@XmlRootElement
public class Employee {
    private int id;
    private String name;
    private String email;

    private List<Address> addresses;

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }

    public List<Address> getAddresses() {
        return addresses;
    }
    public void setAddresses(List<Address> addresses) {
        this.addresses = addresses;
    }

    @SuppressWarnings("unused")
    @XmlRegistry
    public static class XMLObjectFactory {
        @XmlElementDecl(scope = Employee.class, name= "id")
        JAXBElement<String> createEmployeeId(String value) {
            return new JAXBElement<String>(new QName("id"), String.class, "100");
        }
        @XmlElementDecl(scope = Employee.class, name= "name")
        JAXBElement<String> createName(String value) {
            return new JAXBElement<String>(new QName("name"), String.class, "Fake Name");
        }
        @XmlElementDecl(scope = Employee.class, name= "email")
        JAXBElement<String> createEmail(String value) {
            return new JAXBElement<String>(new QName("email"), String.class, value);
        }

        @XmlElementDecl(scope = Employee.class, name= "addresses")
        JAXBElement<List> createAddresses(List value) {
            return new JAXBElement<List>(new QName("addresses"), List.class, value);
        }
    }
}

子类 - Address.java

package com.test.jaxb.dto;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;

import com.test.jaxb.Employee;

@XmlRootElement
public class Address {
    private String addressLine1;
    private String addressLine2;
    private String addressLine3;
    public String getAddressLine1() {
        return addressLine1;
    }
    public void setAddressLine1(String addressLine1) {
        this.addressLine1 = addressLine1;
    }
    public String getAddressLine2() {
        return addressLine2;
    }
    public void setAddressLine2(String addressLine2) {
        this.addressLine2 = addressLine2;
    }
    public String getAddressLine3() {
        return addressLine3;
    }
    public void setAddressLine3(String addressLine3) {
        this.addressLine3 = addressLine3;
    }

    @SuppressWarnings("unused")
    @XmlRegistry
    private static class XMLObjectFactory {
        @XmlElementDecl(scope = Employee.class, name= "addressLine1")
        JAXBElement<String> createAddressLine1(String value) {
            return new JAXBElement<String>(new QName("addressLine1"), String.class, value);
        }
        @XmlElementDecl(scope = Employee.class, name= "addressLine2")
        JAXBElement<String> createAddressLine2(String value) {
            return new JAXBElement<String>(new QName("addressLine2"), String.class, value);
        }
        @XmlElementDecl(scope = Employee.class, name= "addressLine3")
        JAXBElement<String> createAddressLine3(String value) {
            return new JAXBElement<String>(new QName("addressLine3"), String.class, value);
        }
    }
}

需要进行反序列化的XML文件 - employee.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
    <id>1</id>
    <name>Vaishali</name>
    <email>Vaishali@example.com</email>
    <addresses>
        <address>
            <addressLine1>300</addressLine1>
            <addressLine2>Mumbai</addressLine2>
            <addressLine3>India</addressLine3>
        </address>
        <address>
            <addressLine1>301</addressLine1>
            <addressLine2>Pune</addressLine2>
            <addressLine3>India</addressLine3>
        </address>
    </addresses>
</employee>

反序列化代码:

package com.test.jaxb;

import java.io.FileReader;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;


public class ObjectFactoryTest {
    public static void main(String[] args) throws Exception {
        FileReader reader = new FileReader("resources/employee.xml");
        JAXBContext context = JAXBContext.newInstance(Employee.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        Object obj = unmarshaller.unmarshal(reader);
        System.out.println(obj);
    }
}
当我使用上述代码解组员工XML时,地址列表未被填充。生成的员工对象只有一个空白的地址列表。我的映射有问题吗?
为了找出问题所在并查看是否实际上是使用 Object Factory(具有 @XMLRegistry 注释)创建了员工对象,我更改了工厂方法中的 id 和 name 的值,但输出没有任何效果。这告诉我 JAXB 实际上没有使用 ObjectFactory,为什么?
我做错了什么吗?任何帮助将不胜感激。
2个回答

17

@XmlRegistry - 它是如何工作的?

@XmlRegistry 用于标记具有 @XmlElementDecl 注释的类。要让您的 JAXB 实现处理此类,您需要确保将其包含在用于引导 JAXBContext 的类列表中。它不能仅仅是您域模型类的静态内部类。

JAXBContext context = JAXBContext.newInstance(Employee.class, Employee.XMLObjectFactory.class);

@XmlElementDecl - 它是如何工作的?

如果字段/属性的值将是一个JAXBElement,那么您需要利用@XmlElementDeclJAXBElement捕获有用的信息:

  • 元素名称,在映射到多个元素为相同类型的选择结构时必需。 如果元素名称不对应于唯一类型,则无法往返文档。
  • JAXBElement可用于表示带有xsi:nil="true"的元素。

XmlObjectFactory

@XmlElementDecl还允许您指定作用域。我稍微修改了你的帖子中的模型。 我引入了一个XmlObjectFactory类,其中包含两个@XmlElementDecl。 两者都指定名称为address。 我利用了scope属性,以便在Employee类中的属性中使用与Address类相对应的@XmlElementDecl

package forum11078850;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;

@XmlRegistry
public class XmlObjectFactory {

    @XmlElementDecl(scope = Employee.class, name = "address")
    JAXBElement<Address> createAddress(Address value) {
        return new JAXBElement<Address>(new QName("address"), Address.class, value);
    }

    @XmlElementDecl(name = "address")
    JAXBElement<String> createStringAddress(String value) {
        return new JAXBElement<String>(new QName("address"), String.class, value);
    }

}

员工

@XmlElementRef 注释将导致属性的值与其根元素名称匹配。可能的匹配项将包括使用 @XmlRootElement@XmlElementDecl 映射的类。

package forum11078850;

import java.util.List;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlType(propOrder = { "id", "name", "email", "addresses" })
public class Employee {
    private int id;
    private String name;
    private String email;

    private List<JAXBElement<Address>> addresses;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @XmlElementWrapper
     @XmlElementRef(name="address")
    public List<JAXBElement<Address>> getAddresses() {
        return addresses;
    }

    public void setAddresses(List<JAXBElement<Address>> addresses) {
        this.addresses = addresses;
    }

}

ObjectFactoryTest

package forum11078850;

import java.io.FileReader;
import javax.xml.bind.*;

public class ObjectFactoryTest {
    public static void main(String[] args) throws Exception {
        FileReader reader = new FileReader("src/forum11078850/input.xml");
        JAXBContext context = JAXBContext.newInstance(Employee.class, XmlObjectFactory.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        Object obj = unmarshaller.unmarshal(reader);

        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(obj, System.out);
    }
}

使用我原来回答中的 Address 类和 input.xml,可以运行此示例。


原始回答

我不确定您如何尝试使用 @XmlRegistry,因此我将重点放在您帖子中的以下部分:

当我使用上面的代码解组员工 XML 时,地址列表未被填充。生成的 employee 对象只有一个空白地址列表。我的映射有什么问题吗?

您的 Address 对象列表被包装在一个组元素 (addresses) 中,因此您需要使用 @XmlElementWrapper 注释来映射此用例。 下面是一个完整的示例:

Employee

package forum11078850;

import java.util.List;
import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlType(propOrder = { "id", "name", "email", "addresses" })
public class Employee {
    private int id;
    private String name;
    private String email;

    private List<Address> addresses;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @XmlElementWrapper
    @XmlElement(name = "address")
    public List<Address> getAddresses() {
        return addresses;
    }

    public void setAddresses(List<Address> addresses) {
        this.addresses = addresses;
    }

}

地址

package forum11078850;

public class Address {
    private String addressLine1;
    private String addressLine2;
    private String addressLine3;

    public String getAddressLine1() {
        return addressLine1;
    }

    public void setAddressLine1(String addressLine1) {
        this.addressLine1 = addressLine1;
    }

    public String getAddressLine2() {
        return addressLine2;
    }

    public void setAddressLine2(String addressLine2) {
        this.addressLine2 = addressLine2;
    }

    public String getAddressLine3() {
        return addressLine3;
    }

    public void setAddressLine3(String addressLine3) {
        this.addressLine3 = addressLine3;
    }

}

ObjectFactoryTest

package forum11078850;

import java.io.FileReader;
import javax.xml.bind.*;

public class ObjectFactoryTest {
    public static void main(String[] args) throws Exception {
        FileReader reader = new FileReader("src/forum11078850/input.xml");
        JAXBContext context = JAXBContext.newInstance(Employee.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        Object obj = unmarshaller.unmarshal(reader);

        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(obj, System.out);
    }
}

输入.xml/输出

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
    <id>1</id>
    <name>Vaishali</name>
    <email>Vaishali@example.com</email>
    <addresses>
        <address>
            <addressLine1>300</addressLine1>
            <addressLine2>Mumbai</addressLine2>
            <addressLine3>India</addressLine3>
        </address>
        <address>
            <addressLine1>301</addressLine1>
            <addressLine2>Pune</addressLine2>
            <addressLine3>India</addressLine3>
        </address>
    </addresses>
</employee>

我发现了几个使用 '@XmlRegistry' 的例子,用法与我使用的方式相同,请看这里。我确实知道可以使用 '@XmlElementWrapper' 来完成这个任务。我的问题更多地是关于理解 '@XmlRegistry' 是什么以及我可以用它做什么。据我所知,我应该能够使用对象工厂(用 '@XmlRegistry' 注释)来取消编组 XML 并将其转换为对象。如果不行,那么它有什么用途?我必须在某个地方注册它才能使用它吗(就像我在示例中演示的那样,我的对象工厂从未被 JAXB 真正使用过)? - gresdiplitude
我的理解可能完全错误,但我认为必须有一种类似于“@XmlElementWrapper”的方式应用于“@XmlRegistry”,因为它是解组XML的另一种方式。毫无疑问,我完全不知道 @XmlRegistry 的工作原理! - gresdiplitude
@Vaishali - 我更新了我的回答,包括一个利用@XmlRegistry@XmlElementDecl的示例。 - bdoughan
+1 对于这些概念的解释。您的博客在过去为我解决了许多JAXB问题,但我没有找到有关此主题的教程,也许您可以写一篇。在接受答案之前需要尝试一下。 - gresdiplitude
@Vaishali - 很高兴听到你发现我的博客有用。我也会把这篇文章转化成一篇文章。我想确保你得到你要找的答案,如果事情还不清楚,请不要放过我 :)。 - bdoughan
1
好的!你的回答很好地解释了何时以及如何使用@XmlRegistry,谢谢! - gresdiplitude

-1
你需要使用一个地址列表对象。在该对象中,你需要添加包含地址行1、地址行2等数据的对象。
 i.e.
 List addrObjList = new List();
 addrObjList.add(object); // Bind an object containing data and add one by one 

有两件事,第一 - 不应该需要,因为Address类本身已经用@XmlRootElement进行了注释,并且拥有自己的对象工厂,JAXB应该能够自动完成... 第二 - 就像我在问题中提到的那样,JAXB似乎根本没有使用对象工厂,所以我对工厂方法所做的任何更改都没有效果。 - gresdiplitude

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接