如何在 JAXB 集合解组中使用 setter

How to use setters in JAXB collections unmarshalling

我不想将 XML 反序列化到我的 POJO,但是出了点问题...

我的 POJO class:

@Builder
@ToString
@AllArgsConstructor
@NoArgsConstructor
@XmlRootElement(name="taxi")
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlType(propOrder = {"id", "name", "phone", "citiesId"})
public class TaxiEntity {
    @Getter @Setter
    private Integer id;

    @Getter @Setter
    private String name;

    @Getter @Setter
    private String phone;

    @Singular("city")
    private Set<Integer> citiesId = new HashSet<>();

    @XmlElementWrapper(name="cities_id")
    @XmlElement(name="city_id")
    public void setCitiesId(Set<Integer> citiesId) {
        System.out.println("setCitiesId()");

        this.citiesId = citiesId;
    }

    public Set<Integer> getCitiesId() {
        System.out.println("getCitiesId()");

        return new HashSet<>(citiesId);
    }
}

编组示例:

JAXBContext context = JAXBContext.newInstance(TaxiEntity.class);

    TaxiEntity entity = TaxiEntity.builder().
        id(5).
        name("my city").
        phone("12345678").
             city(1).
             city(5).
    build();
    
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            marshaller.marshal(entity, new File("entity.xml"));

XML 输出:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<taxi>
    <id>5</id>
    <name>my city</name>
    <phone>12345678</phone>
    <cities_id>
        <city_id>1</city_id>
        <city_id>5</city_id>
    </cities_id>
</taxi>

解组示例:

JAXBContext context = JAXBContext.newInstance(TaxiEntity.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
TaxiEntity entity = (TaxiEntity) unmarshaller.unmarshal(new File("entity.xml"));
System.out.println(entity);

控制台输出:

getCitiesId()
getCitiesId()
TaxiEntity(id=5, name=my city, phone=12345678, citiesId=[])

Process finished with exit code 0

如您所见,citiesId 是空的。 发生这种情况是因为 JAXB 解组调用 getter (在我的例子中是字段的副本) 并尝试将值设置到集合的副本中。 如何让它创建一个集合并通过 setter?

进行设置

P.S。在我的真实业务对象中,我从数据库实体中收集了 getter 中的 ID,并且无法在 getter.

中收集 return

谢谢!

----上次编辑-----

import java.io.File;
import java.util.Set;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import lombok.Builder;
import lombok.Singular;
import lombok.ToString;

@Builder
@ToString
@XmlRootElement(name = "taxi")
@XmlType(name="taxi", propOrder = { "id", "name", "phone", "citiesId" })
public class TaxiEntity {
    
    private Integer id;
    private String name;
    private String phone;

    @Singular("city")
    private Set<Integer> citiesId;

    public TaxiEntity() {
        
    }
    
    public TaxiEntity(Integer id, String name, String phone, Set<Integer> citiesId) {
        
        System.out.println("Hello");
        this.id = id;
        this.name = name;
        this.phone = phone;
        this.citiesId = citiesId;
    }

    
    @XmlElementWrapper(name = "cities_id")
    @XmlElement(name = "city_id")
    public void setCitiesId(Set<Integer> citiesId) {
        System.out.println("I should be calling during deserialization" + citiesId);
        
        this.citiesId = citiesId;
    }

    @XmlElement
    public void setId(Integer id) {
        this.id = id;
    }
    
    @XmlElement
    public void setName(String name) {
        this.name = name;
    }
    
    @XmlElement
    public void setPhone(String phone) {
        this.phone = phone;
    }

    
    public Integer getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public String getPhone() {
        return phone;
    }
    
    public Set<Integer> getCitiesId() {
        
        System.out.println("Calling getter " + this.citiesId);
        
        return citiesId;
    }
    
    

    public static void main(String[] args) {

        try {
            JAXBContext context = JAXBContext.newInstance(TaxiEntity.class);
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

            Unmarshaller unmarshaller = context.createUnmarshaller();            

            TaxiEntity entity = TaxiEntity.builder().id(5).name("my city").phone("12345678").city(1).city(5).build();

            marshaller.marshal(entity, new File("C:/whee/entity.xml"));
            
            System.out.println("Unmarshalling now ------");
            
            TaxiEntity taxEntityWithSettersGetters = (TaxiEntity) unmarshaller.unmarshal(new File("C:/whee/entity.xml"));
            
            System.out.println(taxEntityWithSettersGetters);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

打印输出:

Hello
Calling getter [1, 5]
Unmarshalling now ------
Calling getter null
I should be calling during deserialization[]
Calling getter [1, 5]
TaxiEntity(id=5, name=my city, phone=12345678, citiesId=[1, 5])

在解组期间,JAXB 检查您的集合是否为空,如果是(它将首次调用 setter 将其初始化为空),您可以在日志中看到。

但是,之后,它将使用其内部逻辑来填充集合 (SET),使用您拥有的 Setter 初始化其类型 (New Set)*,并使用 Set.add (xyz);添加 (1),然后添加 (5)。

调用的 JAXB 逻辑位于 class:

public abstract class Lister<BeanT,PropT,ItemT,PackT> {

//startPacking正在调用初始化集合Set所以它是空的

public T startPacking(BeanT bean, Accessor<BeanT, T> acc) throws AccessorException {
    T collection = acc.get(bean);
    if(collection==null) {
        collection = ClassFactory.create(implClass);
        if(!acc.isAdapted())
            acc.set(bean,collection);
    }
    collection.clear();
    return collection;
}

//正确的方法,这会在之后调用(在您的任何 TaxiEntity 逻辑之前),以执行 addToPack(1)、addToPack(5),<--- 现在您的 Set 有 [1,5]

public void addToPack(T collection, Object o) {
    collection.add(o);
}

然后,你在日志中看到,它调用了 getCitiesIds(),你会神奇地看到它有 [1,5]

这是 JAXB 处理集合的方式。所有其他元素,它们适当的 Setter 被调用。

看,JAXB does not call Setter method

你需要想出不同的方法,而不是依赖于 getter/setter。它完成了从 XML 文件中解组对象的工作,其余逻辑可以用外部方法编写。