有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

我不想将XML反序列化到我的POJO中,但有些地方做错了

我的POJO课程:

@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进行设置

另外,在我真正的业务对象中,我在getter中从DB实体收集ID,并且不能在getter中返回集合

谢谢


共 (1) 个答案

  1. # 1 楼答案

    上次编辑-

    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将其初始化为空),您可以在日志中看到这一点

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

    调用的JAXB逻辑可以在类中找到:

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

    //StartPack正在调用以初始化集合集,使其为空

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

    //正确的方法是,这会在之后(在任何实体逻辑之前)调用,以执行addToPack(1)、addToPack(5)和<;-现在你的布景有[1,5]

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

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

    这就是JAXB处理集合的方式。所有其他元素,它们的适当设置器被称为

    JAXB does not call Setter method

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