How to create java object from 'anyType' returned from service using JAXB?
Asked Answered
C

4

27

A web service is returning an object defined by the WSDL to be:

<s:complexType mixed="true"><s:sequence><s:any/></s:sequence></s:complexType>

When I print out this object's class info, it comes up as:

class com.sun.org.apache.xerces.internal.dom.ElementNSImpl

But I need to unmarshall this object as an object of the following class:

@XmlAccessorType(XmlAccessType.FIELD) 
@XmlType(name = "", propOrder = {
        "info",
        "availability",
        "rateDetails",
        "reservation",
        "cancellation",
        "error" }) 
@XmlRootElement(name = "ArnResponse") 
public class ArnResponse { }

I know the response is correct, since I know how to marshall this object's XML:

Marshaller m = jc.createMarshaller();
m.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
m.marshal(rootResponse, System.out);

Which prints out:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:SubmitRequestDocResponse xmlns:ns2="http://tripauthority.com/hotel">
    <ns2:SubmitRequestDocResult>
        <!-- below is the object I'm trying to unmarshall -->
        <ArnResponse>
            <Info />
            <Availability>
                <!-- etc--> 
             </Availability>
        </ArnResponse>
    </ns2:SubmitRequestDocResult>
</ns2:SubmitRequestDocResponse>

How can I turn the ElementNSImpl object I'm seeing into the ArnResponse object I know it represents?

Additionally, I'm running on AppEngine, where file access is restricted.

Thanks for any help

Update:

I've added the @XmlAnyElement(lax=true) annotation, like so:

  @XmlAccessorType(XmlAccessType.FIELD)
  @XmlType(name = "", propOrder = {
      "content"
  })
  @XmlSeeAlso(ArnResponse.class)
  public static class SubmitRequestDocResult {

    @XmlMixed
    @XmlAnyElement(lax = true)
    protected List<Object> content;

But it doesn't make any difference.

Is this something to do with the fact that the content is a List?

Here's the code where I'm trying to access the content after getting it back from the server:

List list = rootResponse.getSubmitRequestDocResult().getContent();

for (Object o : list) {
  ArnResponse response = (ArnResponse) o;
  System.out.println(response);
}

Which has the output:

Jan 31, 2012 10:04:14 AM com.districthp.core.server.ws.alliance.AllianceApi getRates SEVERE: com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to com.districthp.core.server.ws.alliance.response.ArnResponse

Answer:

axtavt's answer did the trick. This worked:

Object content = ((List)result.getContent()).get(0);
JAXBContext context = JAXBContext.newInstance(ArnResponse.class);
Unmarshaller um = context.createUnmarshaller();
ArnResponse response = (ArnResponse)um.unmarshal((Node)content);
System.out.println("response: " + response);
Cranio answered 31/1, 2012 at 13:23 Comment(2)
I have copies of all the schemas for the response objects and the WSDL, if it helps to know that.Cranio
Note that the Node used here is org.w3c.dom.Node.Coagulant
B
22

You can pass that object to Unmarshaller.unmarshal(Node), it should be able to unmarshal it.

Boeotian answered 31/1, 2012 at 15:9 Comment(0)
U
5

You can use @XmlAnyElement(lax=true). This will convert XML with known root elements (@XmlRootElement or @XmlElementDecl) to domain objects. For an example see:

Unpleasant answered 31/1, 2012 at 14:53 Comment(1)
I checked but the annotation was already there. Please take a look at my updated question. Thanks again for your help, and I've come across your blog before and your other answers here on SO. Your work has already been helping me a lot.Cranio
R
2

From what I've found with working with XML, anyType could represent any object, so the closest thing you can map it back to is java.lang.Object. (Besides that fact that anyType could technically be a security hole, allowing somebody to inject anything, including a malicious binary into that spot, and nothing will stop it since your schema allows it.)

You are best off to change your schema to allow a mapping to your custom object. This is cleaner both from a programming perspective, a consumption perspective, and a security perspective.

Pending you can't do that, I would recommend storing the type as an attribute of your element. You may need to write custom code then to help you convert the anyType back to that object, but at least you know its type then.

My two cents based off of my experience (mostly in the realm of integration).

Rubellite answered 31/1, 2012 at 14:50 Comment(1)
Yeah, if I could, I wouldn't use anyType, but we're integrating with someone else's schema, so we're stuck with it.Cranio
R
0

I know this question is already answered, but if you are parsing a complex object, the problem can be solved if you add more of your desired result classes when you initialize your JAXBContext object.

For example: JAXBContext context = JAXBContext.newInstance(ArnResponse.class,ArnResponseDetail.class);

Reinhardt answered 19/6, 2024 at 1:54 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.