@XmlElement with multiple names

2019-02-20 03:06发布

问题:

I have a situation here, trying to act as a gateway between two APIs. What I need to do, is:

  1. make a request to an APIa;
  2. parse (marshal) the XML response into an java object;
  3. make little changes to it;
  4. and then give a response in XML (unmarshal) to the other end (APIb).

The thing is that I use the same object to parse the API response and to send the response to the other end.

public class ResponseAPI{

@XmlElement(name="ResponseCode") //I receive <ResponseCode> but I need to send <ResultCode>
private String responseCode;

//getter and setter
}

as the comment says: I receive but I need to send

Is there a way to get this done without having to create another extra class which carries ResultCode?

thanks in advance!

回答1:

Note:

The answer given by Ilya works but isn't guaranteed to work across all implementations of JAXB or even across versions of a single JAXB implementation. The @XmlElements annotation is useful when the decision of which element to marshal depends on the type of the value (see: http://blog.bdoughan.com/2010/10/jaxb-and-xsd-choice-xmlelements.html). In your use case both the ResponseCode and ResultCode elements correspond to type String, unmarshalling will always work fine, but the choice of which element to output is arbitrary. Some JAXB Impls may have last specified wins, but others could easily have first wins.


You could do the following by leveraging @XmlElementRef.

Java Model

ResponseAPI

We will change the responseCode property from type String to JAXBElement<String>. The JAXBElement allows us to store the element name as well as the value.

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class ResponseAPI{

    @XmlElementRefs({
        @XmlElementRef(name = "ResponseCode"),
        @XmlElementRef(name = "ResultCode")
    })
    private JAXBElement<String> responseCode;

    public JAXBElement<String> getResponseCode() {
        return responseCode;
    }

    public void setResponseCode(JAXBElement<String> responseCode) {
        this.responseCode = responseCode;
    }

}

ObjectFactory

The @XmlElementRef annotations we used on the ResponseAPI class correspond to @XmlElementDecl annotations on a class annotated with @XmlRegistry. Traditionally this class is called ObjectFactory but you can call it anything you want.

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;

@XmlRegistry
public class ObjectFactory {

    @XmlElementDecl(name="ResponseCode")
    public JAXBElement<String> createResponseCode(String string) {
        return new JAXBElement<String>(new QName("ResponseCode"), String.class, string);
    }

    @XmlElementDecl(name="ResultCode")
    public JAXBElement<String> createResultCode(String string) {
        return new JAXBElement<String>(new QName("ResultCode"), String.class, string);
    }

}

Demo Code

input.xml

<responseAPI>
    <ResponseCode>ABC</ResponseCode>
</responseAPI>

Demo

When creating the JAXBContext we need to ensure that we include the class that contains the @XmlElementDecl annotations.

import java.io.File;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(ResponseAPI.class, ObjectFactory.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("Scratch/src2/forum24554789/input.xml");
        ResponseAPI responseAPI = (ResponseAPI) unmarshaller.unmarshal(xml);

        ObjectFactory objectFactory = new ObjectFactory();
        String responseCode = responseAPI.getResponseCode().getValue();
        JAXBElement<String> resultCodeJAXBElement = objectFactory.createResultCode(responseCode);
        responseAPI.setResponseCode(resultCodeJAXBElement);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(responseAPI, System.out);
    }

}

Output

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<responseAPI>
    <ResultCode>ABC</ResultCode>
</responseAPI>


回答2:

You can try next solution using @XmlElements annotaion

@XmlAccessorType(XmlAccessType.FIELD)
public class ResponseAPI 
{
   @XmlElements(
      {
         @XmlElement(name = "ResponseCode"),
         @XmlElement(name = "ResultCode")
      })
   private String responseCode;
   // ...
}

In this case both ResponseCode and ResultCode will be used during unmarshalling (xml -> object) and only ResultCode during marshalling (object -> xml).

So you can unmarshall XML like

<responseAPI>
    <ResponseCode>404</ResponseCode>
</responseAPI>  

After marshalling object will looks like

<responseAPI>
    <ResultCode>404</ResultCode>
</responseAPI>