如何使用 jaxb 从两个相似的 xml 文档生成一个对象

How to generate one objects from two similar xml documents with jaxb

我正在尝试与 xml 中的某些提供程序 REST API 集成。我有两个请求和两个非常相似的响应:Operation 响应和 Check响应。

运算:

<Response>
<ReturnCode>0</ReturnCode>
 <ReturnMessage>OK</ReturnMessage>
 <Commands>
  <OperationResponseCommand>
   <ResultCode>412</ResultCode>
   <ResultMessage>Some message hear</ResultMessage>
   <OperationId>125206188472552900</OperationId>
   <Id>14507921</Id>
  </OperationResponseCommand>
 </Commands>
</Response>

检查:

<Response>
 <ReturnCode>0</ReturnCode>
 <ReturnMessage>OK</ReturnMessage>
 <Commands>
  <CheckResponseCommand>
   <ResultCode>412</ResultCode>
   <ResultMessage>Some message hear</ResultMessage>
   <OperationId>125206188472552900</OperationId>
  </CheckResponseCommand>
 </Commands>
</Response>

如您所见,不同之处在于:

  1. <OperationResponseCommand> 而不是 <CheckResponseCommand> 在第一个 xml.
  2. 里面还有一个标签<Id> <OperationResponseCommand>.

我有两个 class 用于 ResponseCommand.

第一个:

    @Data
    @XmlRootElement(name = "Response")
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Response {
    
        @XmlElement(name = "ReturnCode")
        private Integer returnCode;
        @XmlElement(name = "ReturnMessage")
        private String returnMessage;
        
        @XmlElementWrapper(name = "Commands")
        @XmlElement(name = "OperationResponseCommand")
        private List<Command> commands = new ArrayList<>();
    }

第二个:

    @Data
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Command {
    
        @XmlElement(name = "ResultCode")
        private Integer resultCode;
        @XmlElement(name = "ResultMessage")
        private String resultMessage;
        @XmlElement(name = "OperationId")
        private String operationId;
        @XmlElement(name = "Id")
        private Integer id;
    }

为了从 xml 构建响应对象,我正在使用这个 class:

    public class JAXBOperations {
    
        public <T> T buildObjectFromXml(Class<T> clazz, String xml) {
    
            try {
    
                StringReader reader = new StringReader(xml);
                Unmarshaller unmarshaller = createUnmarshaller(clazz);
    
                return (T) unmarshaller.unmarshal(reader);
    
            } catch (JAXBException e) {
                throw new RuntimeException("Exception has been occurred while creating unmarshaller to parse xml to object");
            }
        }
    
        public <T> String buildXmlFromObject(T objectToXml) {
    
            try {
    
                StringWriter writer = new StringWriter();
                Marshaller marshaller = createMarshaller(objectToXml.getClass());
                marshaller.marshal(objectToXml, writer);
    
                return writer.toString();
    
            } catch (JAXBException e) {
                throw new RuntimeException("Exception has been occurred while creating marshaller to parse object to xml");
            }
        }
    
        private <T> Marshaller createMarshaller(Class<T> clazz) throws JAXBException {
    
            JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
            return jaxbContext.createMarshaller();
        }
    
        private <T> Unmarshaller createUnmarshaller(Class<T> clazz) throws JAXBException {
    
            JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
            return jaxbContext.createUnmarshaller();
        }
    }

方法buildObjectFromXml。它适用于第一个 xml 响应。我有一个这样的对象:

Response(
 returnCode=0, returnMessage=OK,  
 commands=[Command( 
  resultCode=417,   
  resultMessage=Processing,  
  operationId=b6619f26-8583-4272-b89d-d1b200109d06,   
  id=157427079)])

但是对于第二个 xml 响应,它不起作用。我有:

Response(
 returnCode=0, 
 returnMessage=OK, 
 commands=[])

我试图更改我的响应 class 的代码。像这样:

    @Data
    @XmlRootElement(name = "Response")
    @XmlAccessorType(XmlAccessType.FIELD)
    public class tResponse {
    
        @XmlElement(name = "ReturnCode")
        private Integer returnCode;
        @XmlElement(name = "ReturnMessage")
        private String returnMessage;
        
        @XmlElementWrapper(name = "Commands")
        @XmlElementRefs({
            @XmlElementRef(name = "OperationResponseCommand", type = Command.class),
            @XmlElementRef(name = "CheckResponseCommand", type = Command.class)})
        private List<Command> commands = new ArrayList<>();
    }

或者这样:

    @Data
    @XmlRootElement(name = "Response")
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Response {
    
        @XmlElement(name = "ReturnCode")
        private Integer returnCode;
        @XmlElement(name = "ReturnMessage")
        private String returnMessage;
    
        @XmlElementWrapper(name = "Commands")
        @XmlElement(name = "OperationResponseCommand")
        private List<Command> operstionCommands = new ArrayList<>();
    
        @XmlElementWrapper(name = "Commands")
        @XmlElement(name = "CheckResponseCommand")
        private List<Command> checkCommands = new ArrayList<>()
    }

但这两个都不行。

我的问题是:我做错了什么?如何更改我的两个 class 的代码以正确转换为对象两种类型的 xml 响应?

代表XML个元素<OperationResponseCommand> 并且 <CheckResponseCommand> 您将需要单独的 classes 这应该是您的 Command classes 的子 classes。 我们称它们为 OperationResponseCommandCheckResponseCommand.

所有命令共有的属性应保留在您的 Commandclass。将此 class 声明为抽象也是一个好主意。

@Data
@XmlAccessorType(XmlAccessType.FIELD)
public abstract class Command {

    @XmlElement(name = "ResultCode")
    private Integer resultCode;

    @XmlElement(name = "ResultMessage")
    private String resultMessage;

    @XmlElement(name = "OperationId")
    private Integer operationId;
}

命令的特定属性应进入子classes:

@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class OperationResponseCommand extends Command {

    @XmlElement(name = "Id")
    private Integer id;
}
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class CheckResponseCommand extends Command {

}

最后,在你的 Response class 你需要增强注释 您的 List<Command> commands 属性,因此 JAXB 将知道需要实例化哪个对象 class 在命令的 XML 元素名称上。 (另见 javadoc of @XmlElements。)

@XmlElementWrapper(name = "Commands")
@XmlElements({
    @XmlElement(name = "OperationResponseCommand", type = OperationResponseCommand.class),
    @XmlElement(name = "CheckResponseCommand", type = CheckResponseCommand.class)
})
private List<Command> commands = new ArrayList<>();