I have generated classed using JAXB from this xsd file
<xs:schema xmlns:tns="http://testwork/"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0" targetNamespace="http://testwork/">
<xs:element name="sayHelloWorldFrom" type="tns:sayHelloWorldFrom"/>
<xs:element name="sayHelloWorldFromResponse" type="tns:sayHelloWorldFromResponse"/>
<xs:complexType name="sayHelloWorldFrom">
<xs:sequence>
<xs:element name="arg0" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="sayHelloWorldFromResponse">
<xs:sequence>
<xs:element name="return" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
This is the generated class
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "sayHelloWorldFrom", namespace = "http://testwork/", propOrder = {
"arg0"
})
public class SayHelloWorldFrom {
protected String arg0;
/**
* Gets the value of the arg0 property.
*
* #return
* possible object is
* {#link String }
*
*/
public String getArg0() {
return arg0;
}
/**
* Sets the value of the arg0 property.
*
* #param value
* allowed object is
* {#link String }
*
*/
public void setArg0(String value) {
this.arg0 = value;
}
}
I have a soap message just like this
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tes="http://testwork/">
<soapenv:Header/>
<soapenv:Body>
<tes:sayHelloWorldFrom>
<!--Optional:-->
<arg0>?</arg0>
</tes:sayHelloWorldFrom>
</soapenv:Body>
</soapenv:Envelope>
I am trying to format this message into class SayHelloWorldFrom, here is the example of my code
public void unmarshalSoapRequest(InputStream is) throws JAXBException {
JAXBContext js = JAXBContext.newInstance(SayHelloWorldFrom.class);
Unmarshaller unmarshaller = js.createUnmarshaller();
SayHelloWorldFrom sayHelloWorldFrom = (SayHelloWorldFrom) unmarshaller.unmarshal(is);
But I have an error in Tomcat logs like
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Envelope"
). Expected elements are (none)
What am I doing wrong? Please, help newbie :-)
Thanx in advnance
You are trying to unmarshall a stream that contains SOAP specific information using a JAXB context that knows nothing about SOAP. It is only able to unmarshall the middle of the SOAP request.
<tes:sayHelloWorldFrom>
<!--Optional:-->
<arg0>?</arg0>
</tes:sayHelloWorldFrom>
You are not supposed to write the unmarshalSoapRequest method. Normally, you'd either to create a class that implements your Web service interface or to write a WSDL and generate the code from the WSDL not from the XSD.
Related
I have an issue generating proper XML/JSON with my WebService, when handling simpleContent tags in the XSD.
First of all, I only have an XSD file (no WSDL), with this content (simplified) :
<xs:complexType name="VerticalDataValue">
<xs:simpleContent>
<xs:extension base="NCADevicePublication:Double">
<xs:attribute name="unit" type="NCADevicePublication:VerticalUnitEnum" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="Double">
<xs:restriction base="xs:double" />
</xs:simpleType>
<xs:simpleType name="VerticalUnitEnum">
<xs:restriction base="xs:string">
<xs:enumeration value="ft"></xs:enumeration>
<xs:enumeration value="m"></xs:enumeration>
</xs:restriction>
</xs:simpleType>
I then generate my class using Xsd.exe.
What I expect for this structure, on the XML output:
<altitudeCoordinate unit="ft">2.1</altitudeCoordinate>
What I get :
<altitudeCoordinate>
<Value>2.1</Value>
<unit>ft</unit>
<unitSpecified>true</unitSpecified>
</altitudeCoordinate>
The same for JSON, I expect :
"altitudeCoordinate": {
"#unit": "ft",
"text": "2.1"
}
But I get :
"altitudeCoordinate":{
"unit": "ft",
"unitSpecified": "true,
"Value": 2.1
I saw on MSDN that the simpleContent restriction is not recognized by Xsd.Exe.
Is there any way to have this kind of "standard" output or .Net just cannot do it?
Thanks
UPDATE
I was able to make it work for my WCF Service. The solution was quite simple... just put [XmlSerializerFormat] at the top of the Interface, and voilĂ .
The reason is that the default serializer (DataContractSerializer) cannot handle xml attributes correctly.
Also, be aware that WCFStorm uses the DataContractSerializer as well : I discovered that even when using the XmlSerializer, I had bad XML in WCFStorm. I then checked with SoapUI, and it was working! (lost a few hours still).
So now, let's make this work on my Web API project, for JSON....
Finally I figured a solution. Just a reminder : my goal was not to touch at the generated class.
For SOAP/XML
As stated in my update, the solution was pretty easy, I just put the following attribute on the WCF interface :
[XmlSerializerFormat]
For REST/JSON
The solution was to define MetadataType classes for the "bothering" classes that were not well converted.
For example, here is how the VerticalDataValue class is defined in the auto-generated file (I stripped the attributes and namespaces):
public partial class VerticalDataValue {
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public VerticalUnitEnum unit;
/// <remarks/>
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool unitSpecified;
/// <remarks/>
[System.Xml.Serialization.XmlTextAttribute()]
public double Value;
}
I created another file, containing all MetadataType classes, defined like following:
[MetadataType(typeof(VerticalDataValueMetaClass))]
public partial class VerticalDataValue { }
public class VerticalDataValueMetaClass
{
[JsonProperty("#unit")]
public VerticalUnitEnum unit;
[JsonIgnore]
public bool unitSpecified
[JsonProperty("text")]
public double value;
}
Be aware that this is only possible if your base class is partial.
Of course, this is high-maintenance code, but at least this does not mess with the auto-generated class.
I have Jersey PUT webservice which takes JSON/xml containing multiple arrays of objects-shown below as input.
{
"a" : [{"p1":"x1"},{"p2":"x2"}],
"b" : [{"q1":"y1"},{"q2":"y2"}],
"c" : [{"r1":"z1"},{"r2":"z2"}]
}
In the service class this gets converted in to JavaBean input param. I have verified the xsd mappings are correct.
The issue is, when I invoke the service with JSON as input, in server side javabean I see only the first value getting populated correctly but the second and third are always null.
In other words, if I pass the json as listed in below different combination scenarios, I see the corresponding results as mentioned.
{"a":[{}], "b":[{}], "c":[{}] }
Only "a" is received in JavaBean. "b" and "c" are received as null.
{"b":[{}], "c":[{}], "a":[{}] }
Only "b" is received in JavaBean. "c" and "a" are received as null.
{"c":[{}], "a":[{}], "b":[{}] }
Only "c" is received in JavaBean. "a" and "b" are received as null.
So it seems I get only first JSON key-value pair correctly no matter in what order I pass from client.
Thanks for helping out on this.
More details below:
This is legacy system and I do not have rights to share the actual code. The system is working fine with input of one-array, and requires to update, so it can accept multiple arrays in JSON as explained in question.
Here is what exactly different parts of the system looks like. Also I have verified that all the xml name-space and POJO properties etc are correct and annotated properly.
1) The CreateABCResource.java which implements PUT method:
//package x.y.z
//import javax.ws.rs.*..etc;
#Path("mypath")
public class CreateABCResource{
#Consumes( { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
#PUT
public AResponse doSomething(CreateABC createABC) {
AResponse x = new AResponse();
//business logic here
return x;
}
}
2) The input POJO CreateABC.java to service method PUT is as below:
//package com.x.y
//import ...;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "CreateABC", namespace = "urn:x", propOrder = {
"abc","def","pqr"
})
#XmlRootElement(name = "createAbc", namespace = "urn:x")
public class CreateABC {
#XmlElement(name = "abc", namespace = "urn:x")
protected List<Abc> abc = null;
#XmlElement(name = "def", namespace = "urn:x")
protected List<Def> def = null;
#XmlElement(name = "pqr", namespace = "urn:x")
protected List<Pqr> pqr = null;
#XmlAttribute(required = true)
protected String a;
#XmlAttribute(required = true)
protected String b;
#XmlAttribute(required = true)
protected String c;
//and so on ...
//getter setters...
}
3) The input JSON from client/fiddler tool for testing:
{
"a": "some value a",
"b": "some value b",
"c": "some value c",
"abc":[{},{}],
"def":[{},{}],
"pqr":[{},{}]
}
4) xsd containing the mappings for JAXB:
<xs:element name="createAbc" type="CreateABC" />
<xs:complexType name="CreateABC">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="abc" type="Abc" />
<xs:element minOccurs="0" maxOccurs="unbounded" name="def" type="Def" />
<xs:element minOccurs="0" maxOccurs="unbounded" name="pqr" type="Pqr" />
</xs:sequence>
<!-- THIS IS COMMENTED, BUT THIS ALSO DIDNOT WORK IN PLACE OF ABOVE sequence
<xs:all>
<xs:element minOccurs="0" maxOccurs="unbounded" name="abc" type="Abc" />
<xs:element minOccurs="0" maxOccurs="unbounded" name="def" type="Def" />
<xs:element minOccurs="0" maxOccurs="unbounded" name="pqr" type="Pqr" />
</xs:all>
-->
<xs:attribute name="a" use="required" type="xs:string" />
<xs:attribute name="b" use="required" type="xs:string" />
<xs:attribute name="c" use="required" type="xs:string" />
</xs:complexType>
<xs:complexType name="Abc">
<!-- CORRET DEFINITON OF Abc is present here -->
</xs:complexType>
<xs:complexType name="Def">
<!-- CORRET DEFINITON OF Def is present here -->
</xs:complexType>
<xs:complexType name="Pqr">
<!-- CORRET DEFINITON OF Pqr is present here -->
</xs:complexType>
Replace below code in input POJO:
#XmlElement(name = "abc", namespace = "urn:x")
protected List<Abc> abc = null;
#XmlElement(name = "def", namespace = "urn:x")
protected List<Def> def = null;
#XmlElement(name = "pqr", namespace = "urn:x")
protected List<Pqr> pqr = null;
with:
protected List<Abc> abc = null;
protected List<Def> def = null;
protected List<Pqr> pqr = null;
and it works.
I have a simple controller, which should return JSON, but is failing to do so. The JSON library is Jackson, configured as a maven dependency. When I make a request using postman, against this url path, I am receiving a 404 error. When I attempt to inspect the JSON Returned, I see "Malformed JSON: Unexpected '<'".
Could someone suggest what it is I am missing / failing to understand? Thank you
#RestController
#RequestMapping("/World/")
public class RestfulController {
#RequestMapping(value = "/Country/", method = RequestMethod.GET, produces="application/json")
public ResponseEntity<Country> findAllCountrys(){
Country c = new Country(1, "Ethiopia", "Addis Abba", "94 Million");
return new ResponseEntity<Country>(c, HttpStatus.OK);
}
}
spring application context
<beans xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.restfulapp.controller"/>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
</map>
</property>
</bean>
You should try this:
#RestController
#RequestMapping("/World")
public class RestfulController {
#RequestMapping(value = "/Country", method = RequestMethod.GET)
public Country findAllCountrys(){
Country c = new Country(1, "Ethiopia", "Addis Abba", "94 Million");
return c;
}
}
The request url: http://yourhost/World/Country
This should return a json of Country
I'm having troubles with handling Exceptions in my apache-cxf client.
I'm expecting an exception that is thrown by the soap call 'Fault_Exception' but I'm always receiving the common SOAPFaultException
Generated classes:
#WebFault(name = "FaultMessage", targetNamespace = "http://a.b.c/ws/")
public class Fault_Exception extends Exception {
private a.b.c.Fault faultMessage;
....
}
and Fault is
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "fault", propOrder = {
"code",
"description",
"stack"
})
public class Fault {
...
}
soap:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>EXCEPTION</faultstring>
<detail>
<Fault xmlns:n1="http://a.b.c/ws/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://a.b.c/ws/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="n1:fault">
<code xmlns="http://a.b.c/ws/">error.code.1</code>
<description xmlns="http://a.b.c/ws/">some description of the error.</description>
<stack xmlns="http://a.b.c/ws/">....</stack>
</Fault>
</detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>
Is there a mismatch between the generated classes and the response ?
Or what else can be the problem of this?
Or is it totally logic that I get a SOAPFaultException? I have no clue how to get the information (code & description) then ...
Kind regards!
Using WSO2 ESB, I'm trying to send an AMQP message to the WSO2 message broker. This AMQP message should have JSON content type.
I expose a Proxy service in WSO2 ESB that gets the message, transforms it to JSON and sends it to the message broker. The proxy service configuration looks like :
<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse" name="json_sample" transports="http" statistics="disable" trace="disable" startOnLoad="true">
<target>
<inSequence>
<property name="OUT_ONLY" value="true" scope="default" type="STRING"/>
<property name="FORCE_SC_ACCEPTED" value="true" scope="axis2"/>
<property name="messageType" value="application/json" scope="axis2" type="STRING"/>
<log level="full"/>
<header name="To"
value="jms:/sample-queue?transport.jms.ConnectionFactoryJNDIName=sampleConnectionFactory&java.naming.factory.initial=org.wso2.andes.jndi.PropertiesFileInitialContextFactory&java.naming.provider.url=repository/conf/jndi.properties&transport.jms.DestinationType=queue"/>
<send/>
</inSequence>
</target>
<publishWSDL>
<definitions name="JsonSample" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.examples.com/wsdl/JsonSample.wsdl" targetNamespace="http://www.examples.com/wsdl/JsonSample.wsdl">
<message name="JsonSampleRequest">
<part name="prop1" type="xsd:string"/>
<part name="prop2" type="xsd:string"/>
</message>
<portType name="JsonSample_PortType">
<operation name="jsonObject">
<input message="tns:JsonSampleRequest"/>
</operation>
</portType>
<binding name="JsonSample_Binding" type="tns:JsonSample_PortType">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="jsonObject">
<soap:operation soapAction="jsonObject"/>
<input>
<soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:examples:jsonsample" use="encoded"/>
</input>
</operation>
</binding>
<service name="JsonSample_Service">
<port binding="tns:JsonSample_Binding" name="JsonSample_Port">
<soap:address location="http://www.examples.com/JsonSample/"/>
</port>
</service>
</definitions>
</publishWSDL>
<description/>
</proxy>
This proxy service works well, transforming the message to JSON, but do not set AMQP message content type to JSON. The message is considered as text.
When I try to inject an AMQP message in the message broker programmatically, using this code, I really have a message with JSON content type :
import java.util.Properties;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSession;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.wso2.andes.client.message.JMSTextMessage;
public class StackOverflowSample {
private static final String QPID_ICF = "org.wso2.andes.jndi.PropertiesFileInitialContextFactory";
private static final String CF_NAME_PREFIX = "connectionfactory.";
private static final String CF_NAME = "qpidConnectionfactory";
private static final String CARBON_CLIENT_ID = "carbon";
private static final String CARBON_VIRTUAL_HOST_NAME = "carbon";
private static final String host = "localhost";
private static final int port = 5675;
private static final String userName = "admin";
private static final String password = "admin";
private static final String queueName = "sample-queue";
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, QPID_ICF);
properties.put(CF_NAME_PREFIX + CF_NAME, getTCPConnectionURL(userName, password));
InitialContext ctx = new InitialContext(properties);
// Lookup connection factory
QueueConnectionFactory connFactory = (QueueConnectionFactory) ctx.lookup(CF_NAME);
QueueConnection queueConnection = connFactory.createQueueConnection();
queueConnection.start();
QueueSession queueSession = queueConnection.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
// Send message
Queue queue = queueSession.createQueue(queueName);
// create the message to send
JMSTextMessage textMessage = (JMSTextMessage) queueSession
.createTextMessage("{\"prop1\":\"value1\",\"prop2\":\"value2\"}");
textMessage.setContentType("application/json");
javax.jms.QueueSender queueSender = queueSession.createSender(queue);
queueSender.send(textMessage);
queueSender.close();
queueSession.close();
queueConnection.close();
}
private static String getTCPConnectionURL(String username, String password) {
// amqp://{username}:{password}#carbon/carbon?brokerlist='tcp://{hostname}:{port}'
return new StringBuffer().append("amqp://").append(username).append(":").append(password).append("#")
.append(CARBON_CLIENT_ID).append("/").append(CARBON_VIRTUAL_HOST_NAME).append("?brokerlist='tcp://")
.append(host).append(":").append(port).append("'").toString();
}
}
Changing textMessage.setContentType("application/json"); to textMessage.setContentType("text/plain"); gives me the same result as using the ESB.
So the question is : how can I configure the ESB to set the AMQP message content type as JSON ?
Thanks
<property name="messageType" value="application/json" scope="axis2" type="STRING"/>
is used to choose message formatter class.
You could have a try with :
<property name="ContentType"
value="application/json"
scope="axis2"
type="STRING"/>