Tuesday, November 5, 2013

HowTo: How to create JAX_WS WebMethod which returns a complex type, by example java.util.HashMap

In simple words, you can't do this directly. The only way is use of workaround.

But one by one. I decided to write this post after reading a few threads on Oracle's Forum. By example this thread: https://forums.oracle.com/message/7015500. I found also many other posts with issues "what to do when WebMethod returns null value instedad of complex type" - which is a typical symptom when you try to return an unacceptable type.

As says Java EE 6 Tutorial:

"JAX-WS delegates the mapping of Java programming language types to and from XML definitions to JAXB. Application developers don’t need to know the details of these mappings but should be aware that not every class in the Java language can be used as a method parameter or return type in JAX-WS."

next we have a list of Schema-to-Java mappings:

XML Schema Type Java Data Type
xsd:string java.lang.String
xsd:integer java.math.BigInteger
xsd:int int
xsd.long long
xsd:short short
xsd:decimal java.math.BigDecimal
xsd:float float
xsd:double double
xsd:boolean boolean
xsd:byte byte
xsd:QName javax.xml.namespace.QName
xsd:dateTime javax.xml.datatype.XMLGregorianCalendar
xsd:base64Binary byte[]
xsd:hexBinary byte[]
xsd:unsignedInt long
xsd:unsignedShort int
xsd:unsignedByte short
xsd:time javax.xml.datatype.XMLGregorianCalendar
xsd:date javax.xml.datatype.XMLGregorianCalendar
xsd:g javax.xml.datatype.XMLGregorianCalendar
xsd:anySimpleType java.lang.Object
xsd:anySimpleType java.lang.String
xsd:duration javax.xml.datatype.Duration
xsd:NOTATION javax.xml.namespace.QName

and Java-to-Schema mappings:

Java Class XML Data Type
java.lang.String xs:string
java.math.BigInteger xs:integer
java.math.BigDecimal xs:decimal
java.util.Calendar xs:dateTime
java.util.Date xs:dateTime
javax.xml.namespace.QName xs:QName
java.net.URI xs:string
javax.xml.datatype.XMLGregorianCalendar xs:anySimpleType
javax.xml.datatype.Duration xs:duration
java.lang.Object xs:anyType
java.awt.Image xs:base64Binary
javax.activation.DataHandler xs:base64Binary
javax.xml.transform.Source xs:base64Binary
java.util.UUID xs:string

If your desired type wasn't listed above, you have to come up with some workaround. For types like lists and maps you can convert those types to array of simple java types. By example:

/**
* We need return Map<String,String> but because we can't do this, 
* we would use workaround and return an two-dimensional array of Strings
*/

@WebMethod()
@WebResult(name="MyMethod",targetNamespace="http://my-java-planet.blogspot.com/my-java-planet")
public String[][] MyMethod(
        @WebParam(name="Symbol",targetNamespace="http://my-java-planet.blogspot.com/my-java-planet") 
        String projectSymbol,             
        @WebParam(name="Year",targetNamespace="http://my-java-planet.blogspot.com/my-java-planet") 
        String projectYear)
{

      try
      {
             LinkedHashMap<String,String> re = getData(projectSymbol, projectYear); 
             
             String ra[][] = new String[re.size()][2]; 
             int i = 0; 
             for(Map.Entry<String,String> entry : re.entrySet())
             { 
                         ra[i][0] = entry.getKey(); 
                         ra[i][1] = entry.getValue(); 
                         i++; 
             } 
             return ra; 
      } 
      catch (Exception ex) 
      { 
             String methodName=Thread.currentThread().getStackTrace()[1].getMethodName();
             logger.severe("Exception "+ex.getMessage()+" in "+methodName );
             String re[][] = new String[][]{{"ERROR",ex.getMessage()}};
      }
} 

1 comment: