Array properties nested in JSON object received as null - json

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.

Related

Unrecognized attribute 'name' when using <remove /> in a ConfigurationElementCollection

I have a custom ConfigurationSection that contains a custom ConfigurationElementCollection with custom ConfigurationElement instances. The <add> and <clear> tags work fine in the configuration file, but using <remove> generates the following exception during my unit test:
System.Configuration.ConfigurationErrorsException: Unrecognized attribute 'name'. Note that attribute names are case-sensitive.
The configuration file is pretty simple. Here's the inner-part of the test section in question:
<exceptionHandling>
<policies>
<clear />
<add name="default" shouldLog="true" shouldShow="true"/>
<add name="initialization" shouldLog="true" shouldShow="true"/>
<add name="security" shouldLog="true" shouldShow="true"/>
<add name="logOnly" shouldLog="true" shouldShow="false"/>
<add name="unhandled" shouldLog="true" shouldShow="true"/>
<add name="test" shouldLog="false" shouldShow="false"/>
<remove name="test"/>
</policies>
</exceptionHandling>
The following is the relevant code for the configuration classes (some has been elided for brevity).
public sealed class ExceptionHandlingSection : ConfigurationSection
{
[ConfigurationProperty("policies", IsDefaultCollection = false)]
[ConfigurationCollection(typeof(PolicyElementCollection), AddItemName = "add", RemoveItemName = "remove", ClearItemsName = "clear")]
public PolicyElementCollection Policies => (PolicyElementCollection)base["policies"];
}
public sealed class PolicyElementCollection : ConfigurationElementCollection
{
// pretty boiler plate
}
public sealed class PolicyElement : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true)]
public string Name
{
get => (string)this["name"];
set => this["name"] = value;
}
// other properties
}
What needs to be done to get <remove> to work as shown in the test configuration file?
The answer to this turns out to be really simple.
I read a few things talking about XML attributes, but fundamentally it's looking for a key property. This can be assigned by a property of the ConfigurationPropertyAttribute. To get the <remove> tag to work all I need to do is change my ConfigurationElement class as follows:
public sealed class PolicyElement : ConfigurationElement
{
[ConfigurationProperty("name", IsKey = true, IsRequired = true)]
public string Name
{
get => (string)this["name"];
set => this["name"] = value;
}
// other properties
}

Default Date value Hibernate

I am inserting some data on a MySQL db without any problem. Now I want to add one more column "date" and I want to insert as default the Date of the insert. How can I do that?
"FbData.hbm.xml" :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.hibernate.FbData" table="dataresponse" catalog="karma">
<id name="idData" type="java.lang.Integer">
<column name="idData" />
<generator class="identity" />
</id>
<property name="likes" type="java.lang.Long">
<column name="likes" not-null="false" unique="false" />
</property>
<property name="shares" type="java.lang.Long">
<column name="shares" not-null="false" unique="false" />
</property>
<property name="time" type="string">
<column name="time" not-null="false" unique="false" />
</property>
<property name="idPage" type="string">
<column name="idPage" not-null="false" unique="false" />
</property>
</class>
</hibernate-mapping>
And my Class with getters and setters:
public class FbData implements Serializable{
private static final long serialVersionUID = 1L;
private Integer idData;
private Long likes;
private Long shares;
private String time;
private String idPage;
}
Thanks in Advance!!!
The Hibernate specific way to do this is to use the #CreationTimestamp annotation.
#Entity
public class SomeEntity {
#CreationTimestamp
private Date date;
}
From the javadoc found here:
Marks a property as the creation timestamp of the containing entity. The property value will be set to the current JVM date exactly once when saving the owning entity for the first time.
Supported property types:
java.util.Date
Calendar
java.sql.Date
Time
Timestamp
If you want to do this using a JPA-specific solution, you can do this two ways:
Apply an Entity Listener handler.
Apply a #PrePersist callback method on the entity
To apply an entity listener:
// A simple interface
public interface CreationTimestampAware {
void setCreationTime(Date date);
Date getCreationTime();
}
// Define the entity listener
public class CreationTimestampListener {
#PrePersist
public void onPrePersist(Object entity) {
// entity implements a CreationTimestampAware interface that exposes
// the single method #setCreationTime which we call here to set
// the value on the entity.
//
// Just annotate the entities you want with this functionality
// and implement the CreationTimestampAware interface
if ( CreationTimestampAware.class.isInstance( entity ) ) {
( (CreationTimestampAware) entity ).setCreationTime( new Date() );
}
}
}
// Link to your entity
#EntityListeners({CreationTimestampListener.class})
public class SomeEntity implements CreationTimeAware {
// specify your date property and implement the interface here
}
If you need the functionality across multiple entities, you can easily hook into this with minimal effort and manage the functionality in one place rather than in multiple entities.
But that's a lot of work for a simple, single entity feature. If that is the case, you can use the other suggested answer with just adding an annotated JPA callback method on the entity itself
#PrePersist
private void onPersistCallback() {
// just set the value here
// this will only ever be called once, on a Persist event which
// is when the insert occurs.
this.date = new Date();
}
You can use #PrePersist annotation to assign current date to the attribute before inserting the row to database
#PrePersist
protected void onCreate() {
if (date == null) {
date = new Date();
}
}
This way, if you don't set the value of the date and try to persist the object to the DB, you'll get current date as a value.

XSD simpleContent with WCF/Web API

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.

WSO2ESB - Sending message to MB with JSON content type

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"/>

javax.xml.bind.UnmarshalException:

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.