#XmlRootElement(name = "request")
#XmlAccessorType(XmlAccessType.FIELD)
#JSONConfigurable
public class InteractionRequest {
#XmlElement(name = "skill")
protected String skillName;
}
#XmlRootElement(name = "request")
#XmlAccessorType(XmlAccessType.FIELD)
#JSONConfigurable
public class InteractionChatRequest extends InteractionRequest {
#XmlElement
#XmlJavaTypeAdapter(LPStringsXmlAdapter.class)
#XmlElementWrapper(name = "preChatLines")
protected List<String> line;
}
And 2 usages:
#PUT
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response postExitSurvey(EntityHolder<InteractionRequest> ent) {
InteractionRequest request = ent.getEntity();
return null;
}
#POST
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response interactionRequest(EntityHolder<InteractionChatRequest> ent) {
InteractionChatRequest params = ent.getEntity();
return null;
}
Now, in both cases, the entity holder holds InterationRequest object which results in a ClassCastException in the second usage.
Any idea why? Shouldn't Jersey cast the entity to the type I declare?
Is hierarchy even possible in this case?
Thanks,
Udi
You have a problem with the JAXB annotations: both InteractionRequest and InteractionChatRequest are annotated with #XmlRootElement(name = "request"). So they have the same root element, which makes it impossible for JAXB to distinguish between them.
Try to change the InteractionChatRequest to #XmlRootElement(name = "chat-request").
Related
I'm trying to integrate with some provider REST API within xml. I have two requests and two very similar responses: Operation response and Check response.
Operation:
<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>
Check:
<Response>
<ReturnCode>0</ReturnCode>
<ReturnMessage>OK</ReturnMessage>
<Commands>
<CheckResponseCommand>
<ResultCode>412</ResultCode>
<ResultMessage>Some message hear</ResultMessage>
<OperationId>125206188472552900</OperationId>
</CheckResponseCommand>
</Commands>
</Response>
As you can see, the differences are:
<OperationResponseCommand> instead of <CheckResponseCommand>
in first xml.
One additional tag <Id> inside
<OperationResponseCommand>.
And I have two classes for Response and for Command.
First:
#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<>();
}
Second:
#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;
}
For building response object from xml I'm using this 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();
}
}
Method buildObjectFromXml. And it works fine for first xml response. I have an object like this:
Response(
returnCode=0, returnMessage=OK,
commands=[Command(
resultCode=417,
resultMessage=Processing,
operationId=b6619f26-8583-4272-b89d-d1b200109d06,
id=157427079)])
But for the second xml response it doesn't works. I have:
Response(
returnCode=0,
returnMessage=OK,
commands=[])
I tried to change the code of my Response class. Like this:
#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<>();
}
Or this:
#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<>()
}
But it both doesn't works too.
My question is: what I'm doing wrong? How can I change code of my two classes to correct converting to object both types of xml responses?
For representing the XML elements <OperationResponseCommand>
and <CheckResponseCommand> you will need separate classes
which should be subclasses of your Command classes.
Let's call them OperationResponseCommand and CheckResponseCommand.
The properties common to all commands should remain in your
Command class. It is also a good idea to declare this class as abstract.
#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;
}
The properties specific for the commands should go into the subclasses:
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class OperationResponseCommand extends Command {
#XmlElement(name = "Id")
private Integer id;
}
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class CheckResponseCommand extends Command {
}
Finally, in your Response class you need to enhance the annotations
of your List<Command> commands property, so that JAXB
will know which object class needs to be instantiated depending
on the XML element name of the commands.
(See also the 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<>();
I am using spring-boot with hibernate validator
I have a controller annotated with #Validated and I want to validate all the params in my APIs.
One of the annotation is not working(I debugged and it just never being checked as if it is not annotated at all).
public MyResponse getPolicyCounters(#RequestBody #Valid MyRequest request) throws Exception {
The MyRequest class looks like this:
#Valid
public class MyRequest{
boolean shouldSumSubRules;
private #Valid RulesSelector rules;
The RuleSelector class is an interface and looks like this:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonSubTypes({
#JsonSubTypes.Type(value = RulesStaticList.class, name = "list"),
#JsonSubTypes.Type(value = RulesDynamicList.class, name = "criteria")
})
#Valid
public interface RulesSelector {
}
The RuleStaticList class looks like this:
#Valid
public class RulesStaticList extends ArrayList<#Valid RuleReference> implements RulesSelector {
}
The RuleRefrence class looks like this:
#JsonDeserialize(using = RuleReferenceDeserializer.class)
#Valid
public interface RuleReference {
RuleReferenceKind getKind();
}
final class RuleReferenceDeserializer extends StdDeserializer<RuleReference> {
public RuleReferenceDeserializer() {
super(RuleReference.class);
}
#Override
public RuleReference deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String fieldName = p.nextFieldName();
switch (fieldName) {
case "tag":
return p.readValueAs(RuleReferenceByTag.class);
case "special":
return p.readValueAs(RuleReferenceBySpecial.class);
default:
throw ctxt.wrongTokenException(p, RuleReference.class, JsonToken.FIELD_NAME, "");
}
}
}
I have 2 implementation for RuleReference one of them is RuleReferenceByTag and it looks like this:
#Valid
public class RuleReferenceByTag implements RuleReference {
#Size(max = 128)
private String tag;
public RuleReferenceKind getKind() { return RuleReferenceKind.Tag; }
}
I added #Valid annotation everywhere I could but it still does not work.
What am I missing?
Other annotation in with different classes it does work but I could not solve this problem :/
I have a response XML that I need to add in a Java object to use it, however this is null when I try to access something from it.
I tried to make an unmarshal of it but without success
The Body of XML received is this.
<ns1:buscaCadastroImobiliarioGeralResponse>
<return xsi:type="ns1:retornoBuscaCadbciGeral">
<cadastros SOAP-ENC:arrayType="ns1:cadastros[1]" xsi:type="ns1:listaCadastros">
<item xsi:type="ns1:cadastros">
<codigo_cadastro >xsi:type="xsd:string">461954</codigo_cadastro>
The code that is executed
BuscaCadastroImobiliarioGeral request = objectFactory.createBuscaCadastroImobiliarioGeral();
Entrada entrada = new Entrada();
entrada.setCodigoCadastro("461954");
request.setEntrada(entrada);
BuscaCadastroImobiliarioGeralResponse response = (BuscaCadastroImobiliarioGeralResponse) client.callWebService("url", request);
System.out.println(response.getReturnResponse());
public class SOAPConnector extends WebServiceGatewaySupport{
public Object callWebService(String url, Object request) {
return getWebServiceTemplate().marshalSendAndReceive(url, request);
}
}
The problem is that when I get getReturnResponse it always comes null. Above is the classes of model
CLASS BuscaCadastroImobiliarioGeralResponse
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"returnResponse"
})
#XmlRootElement(name = "buscaCadastroImobiliarioGeralResponse")
public class BuscaCadastroImobiliarioGeralResponse {
#XmlElement(name = "return", required = true)
private ReturnResponse returnResponse;
public ReturnResponse getReturnResponse() {
return returnResponse;
}
public void setReturnResponse(ReturnResponse returnResponse) {
this.returnResponse = returnResponse;
}
}
CLASS ReturnResponse
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "return", propOrder = {
"cadastros"
})
#XmlRootElement(name = "return")
#XmlSeeAlso(ReturnResponse.class)
public class ReturnResponse {
#XmlElement(required = true)
private List<Cadastros> cadastros;
public List<Cadastros> getCadastros() {
return cadastros;
}
public void setCadastros(List<Cadastros> cadastros) {
this.cadastros = cadastros;
}
}
CLASS Cadastros
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "cadastros", propOrder = {
"item"
})
#XmlRootElement(name = "cadastros")
#XmlSeeAlso(Cadastros.class)
public class Cadastros {
#XmlElement
private Item item;
public Item getItem() {
return item;
}
public void setItem(Item item) {
this.item = item;
}
}
Problema relacionado: Spring Web service unmarshalling not happening correctly
I am using Jersey and face an issue about how to parse a complex object to xml format, please help me about it, many thanks.
Here is the detail.
First, I make a entity container object like below:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class RestResponse {
//It can be any kinds of type, collection, single object etc
private Object data;
//... still have many properties
public RestResponse() {
}
public RestResponse(Object data) {
this.data = data;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
And here is one of my entity class:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Entity1{
private String name;
private Map<String, Object> otherData = new HashMap<String, Object>();
public Entity1(){
this.name = "aaa";
otherData.put("address", "XXXXX");
otherData.put("age", 13);
//more...
this.otherData = otherData
}
public Entity1(String name, Integer age){
this.name = "aaa";
otherData.put("address", "XXXXX");
otherData.put("age", age);
this.otherData = otherData
}
public String getName() {
return name;
}
public Map<String, Object> getOtherData() {
return otherData;
}
}
Here is my resource class:
#Path("/test")
public class EntityResource{
#GET
#Path("test1")
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response test1() {
Entity1 entity = new Entity1();
return Response.ok(new RestResponse(entity)).build();
}
#GET
#Path("test2")
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response test2() {
List entities = new ArrayList<Entity1>();
entities.add(new Entity1("E1"));
entities.add(new Entity1("E2"));
return Response.ok(new RestResponse(entities)).build();
}
}
Configure jersey with above code, it works fine when I require json format response, but for xml format response, I always get 500 error, am I missing something?
After some research, I found the solution, the answer is simple, I register JacksonXMLProvider.class as a XML provider, hope this can help other people.
I have a class annotated as follows:
#XmlRootElement(name="response")
#XmlType(propOrder={"paymentid",
"result",
"responsecode",
"authorizationcode",
"merchantorderid",
"rrn",
"cardcountry",
"cardtype"})
public class MOTOResponseIn {
...
}
The root element of the mapped XML could be also be error beside
response.
How can I annotate the class so that both root elements are allowed?
In this case #XmlRootElement can not be used.
You have to use ObjectFactory.
The #XmlElementDecl annotation is used to represent root elements that correspond to named complex types. It is placed on a factory method in a class annotated with #XmlRegistry (when generated from an XML schema this class is always called ObjectFactory). The factory method returns the domain object wrapped in an instance of JAXBElement
Hope this url will help.
https://dzone.com/articles/jaxb-and-root-elements
With a single class and #XmlRootElement it is not possible.
However, in case you don't want to mess with ObjectFactory, for a quick workaround you can use abstract and concrete classes. (Assuming the only difference is the root element)
Example:
#XmlAccessorType(XmlAccessType.FIELD)
public abstract class MOTOabstract{
#XmlAttribute
private String paymentid;
#XmlAttribute
private String result
#XmlAttribute
private String responsecode;
...
}
#XmlRootElement(name="response")
#XmlAccessorType(XmlAccessType.FIELD)
public class MOTOresponse extends MOTOabstract{}
#XmlRootElement(name="error")
#XmlAccessorType(XmlAccessType.FIELD)
public class MOTOerror extends MOTOabstract{}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"id",
"name",
"serviceAttrs"
})
#XmlSeeAlso({ AddGroup.class, AddGroupRequest.class })
public class AddGroupAbstract {
#XmlElement(required = true)
protected String id;
#XmlElement(required = true)
protected String name;
#XmlElement(required = true)
protected ServiceAttrs serviceAttrs;
...
}
#XmlRootElement(name = "addGroup")
public class AddGroup extends AddGroupAbstract {}
#XmlRootElement(name = "addGroupRequest")
public class AddGroupRequest extends AddGroupAbstract {}
#Endpoint
public class GroupEndpoint {
private final GroupService groupService;
private final ServiceService serviceService;
private final RestTemplate restTemplate;
public GroupEndpoint(GroupService groupService, ServiceService serviceService, RestTemplate restTemplate) {
this.groupService = groupService;
this.serviceService = serviceService;
this.restTemplate = restTemplate;
}
#PayloadRoots({
#PayloadRoot(namespace = SoapConstants.NAMESPACE_ACCOUNT_URI, localPart = "addGroup"),
#PayloadRoot(namespace = SoapConstants.NAMESPACE_ACCOUNT_URI, localPart = "addGroupRequest")
})
#ResponsePayload
public AddGroupResponse addGroup(#RequestPayload AddGroupAbstract request) {
AddGroupResponse response = new AddGroupResponse();
...
}
}