JAXB XMLAdapter:有没有办法将此方法转换为 JAXB XmlAdapter

问题描述

我有一个 JSON 文件,我正在尝试使用 XML 注释方法将其转换为 JAXB。现在一切正常,我可以将 JSON 转换为 XML。现在我正在尝试稍微重构代码,以便我的类看起来干净。因此,我试图删除我的 class 中存在的方法并将其设为 JAXB XMLAdapter,以便其他类可以重用它。

基本上我想将 XMLSupport 方法CarInfo 类移动到 XMLAdapter。当我将它们移动到 CarInfo 时,我不确定如何填充 XMLAdapter 对象。

以下是我的 JSON 文件(为了简单起见已对其进行了修改):

{
   "brand": "Ferari","build": "Italy","engine": "Mercedes","year": "2021"
   
}

以下是我希望 XML 提供的 JAXB:(注意 carInfo 中不存在的 JSON 标记,但我需要在 XML匹配标准 XSD)

<?xml version="1.0"?>
<Car>
    <brand>Ferari</brand>
    <build>Italy</build>
    <carinfo>
        <engine>Mercedes</engine>
        <year>2021</year>
    </carinfo>
</Car>

以下是我拥有的类:(与 JSON 元素匹配的 Car 类)

@XmlAccessorType(XmlAccesstype.FIELD)
@XmlTransient
@XmlSeeAlso({MyCar.class});
public class Car{
    private String brand;
    private String build;
    
    @XmlTransient
    private String engine;

    @XmlTransient
    private String year;

    //Getter,Setters and other consturctiores ommited
}

以下是通过添加 MYCar 标记构建 XMLcarInfo 类:

@XmlAccessorType(XmlAccesstype.FIELD)
@XmlRootElement(name = "Car")
@XmlType(name = "Car",propOrder = {"brand","build","carInfo"})
public class MyCar extends Car{
    
    @XmlElement(name="carInfo")
    private CarInfo carInfo;
    
    public MyCar xmlSupport() {
        if(carInfo == null){
            carInfo = new Carinfo();
        }
        
        carInfo.setEngine(getEngine);
        carInfo.setYear(getYear());
        return this;
    }
}

以下是我的 CarInfo 类,它充当围绕 additional 类构建 MyCar 标签的助手:

@XmlAccessorType(XmlAccesstype.FIELD)
@XmlType(propOrder = {"engine","year"})
public class Carinfo{
    private String engine;
    private String year;
    //Getter,Setters and other consturctiores ommited
}

以下是我的 Main 类,它通过使用 XML

实际上构建了 JAXBCOntext
public class Main{
    public static void main(String[] args){
        JAXBContext context = JAXBContext.newInstance(MyCar.class);
        Marshaller mar = context.createMarshaller();
        mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE);
        mar.marshal((MyCar).xmlSupport(),System.out);
        System.out.println("-----------------");
    }
}

现在回到我的主要问题: 正如我们从 MyCar 类中看到的,我有 XMLSupport 方法,它实际上填充了 CarInfo 对象,然后使用该方法创建了 XML。有什么办法可以将其移至 XMLAdapter

我尝试创建 XMLAdapter,但不确定如何从适配器填充 CarInfo 对象:

public class MyCar extends Car{
    
    @XmlElement(name="carInfo")
    @XmlJavaTypeAdapter(ExtensionAdapter.class)
    @XmlElement(name = "carInfo")
    private CarInfo carInfo;
}

以下是我尝试过的 Adapter 课程: public class ExtensionAdapter extends XmlAdapter {

    @Override
    public CarInfo unmarshal(CarInfo valueType) throws Exception {
        System.out.println("UN-MARSHALLING");
        return null;
    }

    @Override
    public CarInfo marshal(CarInfo boundType) throws Exception {
        System.out.println("MARSHALLING");
        System.out.println(boundType);
        //I get boundType as NULL so I am not sure how to convert the xmlSupport Method to Adapter so I can use this adapter with multiple class
        return null;
    }
}

解决方法

你不需要任何适配器,你只需要一个定义良好的 POJO。

诀窍是使用 getter 和 setter,而不是字段访问,所以我们可以进行委托,然后使用 @JsonIgnore@XmlTransient 来控制哪些 getter/setter 方法是用于 JSON 与 XML。

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@XmlRootElement(name = "Car")
@XmlType(propOrder = { "brand","build","carinfo" })
@JsonPropertyOrder({ "brand","engine","year" })
public final class Car {

    @XmlType(propOrder = { "engine","year" })
    public static final class Info {
        private String engine;
        private String year;

        public String getEngine() {
            return this.engine;
        }
        public void setEngine(String engine) {
            this.engine = engine;
        }

        public String getYear() {
            return this.year;
        }
        public void setYear(String year) {
            this.year = year;
        }

        @Override
        public String toString() {
            return "Info[engine=" + this.engine + ",year=" + this.year + "]";
        }
    }

    private String brand;
    private String build;
    private Info carinfo;

    public Car() {
        // Nothing to do
    }
    public Car(String brand,String build,String engine,String year) {
        this.brand = brand;
        this.build = build;
        this.carinfo = new Info();
        this.carinfo.setEngine(engine);
        this.carinfo.setYear(year);
    }

    public String getBrand() {
        return this.brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getBuild() {
        return this.build;
    }
    public void setBuild(String build) {
        this.build = build;
    }

    @JsonIgnore // For XML,not JSON
    public Info getCarinfo() {
        if (this.carinfo == null)
            this.carinfo = new Info();
        return this.carinfo;
    }
    public void setCarinfo(Info info) {
        this.carinfo = info;
    }

    @XmlTransient // For JSON,not XML
    public String getEngine() {
        return getCarinfo().getEngine();
    }
    public void setEngine(String engine) {
        getCarinfo().setEngine(engine);
    }

    @XmlTransient // For JSON,not XML
    public String getYear() {
        return getCarinfo().getYear();
    }
    public void setYear(String year) {
        getCarinfo().setYear(year);
    }

    @Override
    public String toString() {
        return "Car[brand=" + this.brand + ",build=" + this.build + ",carinfo=" + this.carinfo + "]";
    }
}

测试

Car car = new Car("Ferari","Italy","Mercedes","2021");

// Generate JSON
ObjectMapper jsonMapper = new ObjectMapper();
jsonMapper.enable(SerializationFeature.INDENT_OUTPUT);
String json = jsonMapper.writeValueAsString(car);

// Generate XML
JAXBContext jaxbContext = JAXBContext.newInstance(Car.class);
Marshaller xmlMarshaller = jaxbContext.createMarshaller();
xmlMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE);
String xml;
try (StringWriter writer = new StringWriter()) {
    xmlMarshaller.marshal(car,writer);
    xml = writer.toString();
}

// Print generated results
System.out.println(car);
System.out.println(json);
System.out.println(xml);

// Parse JSON
Car carFromJson = jsonMapper.readValue(json,Car.class);
System.out.println(carFromJson);

// Parse XML
Unmarshaller xmlUnmarshaller = jaxbContext.createUnmarshaller();
Car carFromXml = xmlUnmarshaller.unmarshal(new StreamSource(new StringReader(xml)),Car.class).getValue();
System.out.println(carFromXml);

输出

Car[brand=Ferari,build=Italy,carinfo=Info[engine=Mercedes,year=2021]]
{
  "brand" : "Ferari","build" : "Italy","engine" : "Mercedes","year" : "2021"
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Car>
    <brand>Ferari</brand>
    <build>Italy</build>
    <carinfo>
        <engine>Mercedes</engine>
        <year>2021</year>
    </carinfo>
</Car>
Car[brand=Ferari,year=2021]]
Car[brand=Ferari,year=2021]]

如您所见,生成的 JSON 和 XML 正是您想要的,最后两行输出表明解析也能正常工作。