We are creating a webservice (CXF-based) driven by a java class (Java2WS) with the following method:
#WebMethod
#RequestWrapper(className = "com.myproject.wrapper.MyRequestWrapper")
#ResponseWrapper(className = "com.myproject.wrapper.MyResponseWrapper")
public MyResponse verifyCode(#WebParam(name = "code") String code) {
...
return new MyResponse("Hello",StatusEnum.okay);
}
I use the wrappers to define the elements of the request resp. response in more detail: the correct element names (which start with an uppercase character), required and optional elements, ...). But I am not sure if this is the right way to do it (there is no in-depth documentation about wrappers, isn't it?)
The class MyResponse:
public class MyResponseWrapper {
private String result;
private ModeEnum status;
// getters and setters
}
The class MyReponseWrapper
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "myResponse")
public class MyResponseWrapper {
#XmlElement(name="Result")
private String result;
#XmlElement(name = "Status")
private StatusEnum status;
public MyResponseWrapper() {
result="fu"; // just for testing
}
// getters and setters
}
Currently I don't understand the Wrappers. When I return an instance of MyReponse, how does the data from MyResponse be injected into MyResponseWrapper respectivly to the SOAP body of the response?
By testing this webservice I can see that an instance of MyResponseWrapper is instantiated and the SOAP body contains the correct elements but with default data (for example: result="fu" instead of "Hello"). I expected that CXF injects matching data from MyResponse to MyResponseWrapper. Is that wrong?
If this is the wrong way to do it:
Wat is the right way to specify the resulting SOAP xml when using Java2WS?
By the way: The above source snippets are just examples taken from our more complex (more fields) classes.
This is the right way to do it.
The request and response wrappers just allow for overriding the xml namespace and element/attribute names for the request/response elements; respectively - which in turn map to the methods used to manage those values.
Ref: http://cxf.apache.org/docs/developing-a-service.html#DevelopingaService-The#RequestWrapperannotation
The #RequestWrapper annotation is defined by the
javax.xml.ws.RequestWrapper interface. It is placed on the methods in
the SEI. As the name implies, #RequestWrapper specifies the Java class
that implements the wrapper bean for the method parameters that are
included in the request message sent in a remote invocation. It is
also used to specify the element names, and namespaces, used by the
runtime when marshalling and unmarshalling the request messages.
The following table describes the properties of the #RequestWrapper
annotation.
localName
Specifies the local name of the wrapper element in the XML
representation of the request message. The default value is the name
of the method or the value of the #WebMethod annotation's
operationName property.
targetNamespace
Specifies the namespace under which the XML wrapper element is
defined. The default value is the target namespace of the SEI.
className
Specifies the full name of the Java class that implements the wrapper
element.
Related
There a json object to be sent to the server, which contains a field:
{"sName":"something"}
In my request model,I declare a var with the same name:
String sName;
But I got null when I receive in the Controller.
I change the field name to lower-case(sname) or add JsonProperty(value="sName") annotation,it work. So where is the problem?
Controller
public ResponseEntity<JSONObject> getComprehensiveInquiryCp(#Validated #RequestBody ComprehensiveInquiryRequestModel body) {
Map<String, Object> content;
JSONObject result = new JSONObject();
String sLicense = body.getSLicense();
...
}
ComprehensiveInquiryRequestModel
#Data
public class ComprehensiveInquiryRequestModel {
...
//#JsonProperty(value = "sLicense")
private String sLicense;
...
}
From top of my head: if you have accessors in that bean, then I think jackson prefers to use them if they exist. And/or Jackson prefers accessors for private fields. As you noticed you can alter that behaviour with Jakson configuration (for example via annotions).
Try:
1. to debug, remove accessor methods and make field public. If that works then change the field back to private and make sure accessor methods are named correctly.
Also single charater prefixes are not a good practise. They can be problematic and confusing. Prefixes in general are lazy and un-Clean Code(tm) practise.
It is important that your setters (and getters) are present and actually conform to the Java naming conventions. A json property named "myFirstName" usually requires a public setter "setMyFirstName(...)" for example. So "sName" needs "setSName()", I guess.
Sure sounds like the naming convnetion might be at fault here.
I'm using dropwizard to create REST API. But I dont understand, how can I configure Jackson to exclude some classes from WRAP_ROOT_VALUE/UNWRAP_ROOT_VALUE features? Right now I get a post request with json body that doesn't include root element name:
{
"identification": "dummyuser",
"password":"dummypass"
}
This should map to java class LoginRequest:
public class LoginRequest {
public String identidication;
public String passwrd;
}
I also get requests for some types that include root element name:
{
"user":{
"id":12345,
"name":"John Doe"
}
}
This should be mapped to:
#JsonRootName("user")
public class User {
...
}
To get root element working I had to include:
environment.getObjectMapper().configure(SerializationFeature.WRAP_ROOT_VALUE, true);
environment.getObjectMapper().configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
but now it applies for all classes. This means that whenever login request comes in, server will throw an error because it expects to see root element name.
Use JsonTypeName with JsonTypeInfo instead of JsonRootName:
#JsonTypeName("user")
#JsonTypeInfo(include= JsonTypeInfo.As.WRAPPER_OBJECT,use= JsonTypeInfo.Id.NAME)
public class User {
...
}
#JsonTypeName
Annotation used for binding logical name that the annotated class has. Used with JsonTypeInfo (and specifically its JsonTypeInfo.use() property) to establish relationship between type names and types.
#JsonTypeInfo
Annotation used for configuring details of if and how type information is used with JSON serialization and deserialization, to preserve information about actual class of Object instances. This is necessarily for polymorphic types, and may also be needed to link abstract declared types and matching concrete implementation.
I have a POJO which contain a field need to be output to XML with tag name "class".
Using Jersey 2.0, if the client request a JSON response, the JSON object output correctly with attribute name "class".
However, if the client request an XML output, Jersey fail with a HTTP 500 internal error.
Checked that the statement causing the error is
#XmlElement(name = "class")
private int vclass;
Removing the XmlElement annotation and allow the XML to use vclass as tag name work fine.
How could I instruct JAXB to use class as tag name ??
Why “class” cannot be use as tag name in JAXB
You can use "class" as a tag name in JAXB.
What Issue You are Probably Hitting
By default JAXB treats public properties as mapped. Since you annotated a field you were most likely getting an exception about a duplicate mapped property.
Exception in thread "main" com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "vclass"
this problem is related to the following location:
at public int forum27241550.Foo.getVclass()
at forum27241550.Foo
this problem is related to the following location:
at private int forum27241550.Foo.vclass
at forum27241550.Foo
Why What You Did Fixed It
You posted the following as an answer:
Finally found out what's wrong.
Don't know why the annotation in variable declaration statement will
cause problem.
Putting the #XmlElement annotation in the setter method work fine.
When you moved the annotation to the property the field was no longer considered mapped, so there was no duplicate mapping problem.
How to Keep the Annotation on the Field
To annotate a field you should use #XmlAccessorType(XmlAccessType.FIELD) on the class.
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
#XmlElement(name = "class")
private int vclass;
public int getVclass() {
return vclass;
}
public void setVclass(int vclass) {
this.vclass = vclass;
}
}
Finally found out what's wrong.
Don't know why the annotation in variable declaration statement will cause problem.
Putting the #XmlElement annotation in the setter method work fine.
I have a service which returns the List, this object varies depends on different scenario.
Can somebody suggest me does jax-ws support this behaviour or do we have any alternative option.
Since that JAX-WS use JAXB for serializate the objects, JAXB need to know the name of the type for marshall or unmarshall. In a standalone environment can deal with this kind of things. However, when dealing with a list of objects, this becomes more complicated.
Moreover, each data type must be defined in the WSDL. The service client must be able to convert the response XML to the data type desired.
If you wish to return different lists of different type, the simplest is to use a wrapper for the response. e.g.
public class ResponseWrapper {
private List<Audio> audios;
private List<Video> videos;
// setters and getters
}
#WebService
public class MediaStore {
#Inject
AudioService audioService;
#Inject
VideoService videoService;
#WebMethod
public ResponseWrapper getCollections(String artistId) {
ResponseWrapper response = new ResponseWrapper();
response.setAudios(audioService.getAudios(artistId));
response.setAudios(videoService.getVideos(artistId));
return response;
}
}
Another way would be to work directly with SOAP messages, but you could avoid doing so.
I am creating a webservice client from a WSDL.
A typical SOAP request to the service looks something like this
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:someGateway">
<soapenv:Header/>
<soapenv:Body>
<urn:send>
<urn:message>
<urn:messageID>1001</urn:messageID>
<urn:messageBody>
<DataContainer>
SOME MORE ELEMENTS
</DataContainer>
</urn:messageBody>
</urn:message>
</urn:send>
</soapenv:Body>
</soapenv:Envelope>
I used JAX-WS to generate the service artefacts and populated my objects as below:
Message message = objectFactory.createMessage();
//Set message ID
String messageID = "123456"
message.setMessageID(messageID );
//Set message Body
MessageBody messageBody = objectFactory.createMessageMessageBody()
The messageBody object has only 1 method messageBody.setAny(value). But i need to place a DataContainer Element inside it.
I have tried passing:
org.w3c.dom.DocumentObject (I get "javax.xml.ws.soap.SOAPFaultException: Failed to process the request.") probbaly due to the xml decleration.
DataContainer object as generated by JAXB from an XSD (I get "[javax.xml.bind.JAXBException: class DataContainer nor any of its super class is known to this context]")
JAXBElement (I get "[javax.xml.bind.JAXBException: class DataContainer is not known to this context]")
What am I doing wrong? Or what do i Need to do to get the DataContainer in the message body
As you mentioned you have messageBody.setAny(value) which means that the XSI:type for MessageBody has been set to anytype. This means you can set any object there, the cavet being JAXB should be able to marshal it within the context defined by the JAX-WS wsdl2java tool. From the error message 'cannot find DataContainer in Conext' it seems like your DataContainer classes are not in the same context.
Here is a workaround for that, you can probably marshal your DataContainer Object into a JAXBElement<String> (or probably simply a String, but I am not sure if that will work) object and then set that into the anyType. This way you won't get Class not know in context as String is a basic JAXB type.
I don't know how you have defined your package structure when you were trying to use point 2 or 3, So I am taking a wild stab here. From the error message, it seems that your separately generated DataContainer Class is not in the same package as the Message and its sub classes. Try to move your DataContainer and its associated classes to the same package as the Message Class and merge the two ObjectFactory Classes together. This should allow JAXB to find DataContainer in the same 'context' as Message.
The error is probably occurring when you make the actual request and JAXB is marshaling the objects to create the request (i.e. JAX-WS is internally calling the JAXB Marshelling service). In this case when you generated the client the JAXBContext was set to the package where Message class is.
Here is a simple tutorial which deals with JAXBContext Marshaling and unmarshaling.
http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/1.6/tutorial/doc/JAXBUsing3.html
Also as per this, you can probably set the anyType to org.w3c.dom.Element and not org.w3c.dom.Document
The secret for using the xs:any of non-included XSD type is #XmlSeeAlso. When you create your JAXB classes from xjc tool you will get an interface defines the #WebService methods. That interface will also be used by client and service implementation. If you won't to modify the auto-generated java files you'd better extends this interface in your package and add #XmlSeeAlso({ExternalClassYouWantToReferTo.class}) to this new interface, ex: IWebServiceInterface
#WebService(name = "IExternalXmlBatchReceive", targetNamespace = "http://External.ServiceContract.BatchReceive")
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
#XmlSeeAlso({
ObjectFactory.class, ExternalClassYouWantToReferTo.class
})
public IWebServiceInterface extends InterfaceYourAutoCreationCode {
#WebMethod(name=...)
......
}
All your Service class and #WebService are implemented from this interface.
When your client call getPort method, you should pass your new implemented interface as the second parameter like:
IWebServiceInterface wi = service.getPort(YOUR_QNAME, IWebServiceInterface.class);
The getPort method will look into the interface which you passed in for the #XmlSeeAlso and initialize its internal JAXBContext.