我已经在互联网上找到了一些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>[email protected]</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时,地址列表不会被填充。生成的雇员对象只有一个空白的地址列表。我的映射有什么问题吗?

为了了解发生了什么,并查看是否实际上是使用对象工厂(具有@XMLRegistry批注)创建员工对象的,我在工厂方法中更改了id和name的值,但是在输出中没有任何作用,这告诉我JAXB实际上没有使用ObjectFactory,为什么?

我要反对这一切错吗?任何帮助,将不胜感激。

最佳答案


@XmlRegistry用于标记具有@XmlElementDecl批注的类。为了使您的JAXB实现过程具有此类,您需要确保将其包含在用于引导JAXBContext的类列表中。仅使其成为您的域模型类之一的静态内部类是不够的:

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



如果字段/属性的值将是JAXBElement,那么您需要利用@XmlElementDeclJAXBElement捕获有用的信息:
  • 元素名称,如果要映射到多个具有相同类型的元素的选择结构,则这是必需的。如果元素名称与唯一类型不对应,那么您将无法往返文档。
  • JAXBElement可用于表示带有xsi:nil="true"的元素。

  • XmlObjectFactory
    @XmlElementDecl还允许您指定范围。我修改了您发布的模型。我介绍了一个具有两个XmlObjectFactory@XmlElementDecl类。两者都指定名称address。我利用了scope属性,以便对于Employee类中的属性,使用与@XmlElementDecl类相对应的Address
    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的,所以我将重点介绍帖子的以下部分:



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

    员工
    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);
        }
    }
    

    input.xml/输出
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <employee>
        <id>1</id>
        <name>Vaishali</name>
        <email>[email protected]</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>
    

    07-26 06:32