I have a nested java map like this
inputMap: {jobId={EndpointReference={ReferenceParameters={ResourceURI=http://schemas.com/wbem/wscim/1/cim-schema/2/Job, SelectorSet={Selector=[JID_502260561923, root/im]}}, Address=http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous}}, returncode=4096, messageId=null, arguments=null, message=null}
which I want to map to java pojo and here is my pojo classes.
#Getter
#Setter
#ToString
public class DMResponseMapper {
#Getter
#Setter
#ToString
public static class GetSysConfigDMResponseMapper {
#JsonProperty("jobId")
private EndpointReferenceMapper endpointReferenceMapper;
private Integer returnCode;
private String messageId;
private String arguments;
private String message;
#Getter
#Setter
#ToString
public static class EndpointReferenceMapper {
#JsonProperty("ReferenceParameters")
private ReferenceParametersMapper referenceParametersMapper;
#JsonProperty("Address")
private String address;
#Getter
#Setter
#ToString
public static class ReferenceParametersMapper {
#JsonProperty("ResourceURI")
private String resourceURI;
#JsonProperty("SelectorSet")
private SelectorSetMapper selectorSetMapper;
#Getter
#Setter
#ToString
public static class SelectorSetMapper {
#JsonProperty("Selector")
private List<String> selector;
}
}
}
}
}
but objectMapper.convertValue(inputMap, GetSysConfigDMResponseMapper.class) is NOT mapping the nested classes.. just the top level fields.
My objectMapper is instantiated like this:
static {
objectMapper = new ObjectMapper();
objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
Response Object is :
DMResponseMapper.GetSysConfigDMResponseMapper(endpointReferenceMapper=DMResponseMapper.GetSysConfigDMResponseMapper.EndpointReferenceMapper(referenceParametersMapper=null, address=null), returnCode=4096, messageId=null, arguments=null, message=null)
Can anyone please suggest, what is wrong here?
Upon debugging this is what I see:
Converted endpointReferenceMapper to type Object.
DMResponseMapper.GetSysConfigDMResponseMapper(endpointReferenceMapper={EndpointReference={ReferenceParameters={ResourceURI=http://schemas.com/wbem/wscim/1/cim-schema/2/Job, SelectorSet={Selector=[JID_502318722705, root/dcim]}}, Address=http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous}}, returnCode=4096, messageId=null, arguments=null, message=null)
The DMResponseMapper pojo needs to follow the structure of your source data more closely.
Your source Map object has the following structure, based on the info in the question:
inputMap:
{
jobId={
EndpointReference={
ReferenceParameters={
ResourceURI=http://schemas.com/wbem/wscim/1/cim-schema/2/Job,
SelectorSet={
Selector=[JID_502260561923, root/im]
}
},
Address=http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
}
},
returncode=4096,
messageId=null,
arguments=null,
message=null
}
So, I adapted your DMResponseMapper pojo class to more closely map to that structure - and I changed the nested class names as well. Here is a summary of the nested classes with their fields for your data:
//
// NOT the actual class - just an overview of the structure!
//
class DMResponseMapper {
private JobId jobId;
private Integer returncode;
private Object messageId;
private Object arguments;
private Object message;
class JobId {
private EndpointReference endpointReference;
class EndpointReference {
private ReferenceParameters referenceParameters;
private String address;
class ReferenceParameters {
private String resourceURI;
private SelectorSet selectorSet;
class SelectorSet {
private List<String> selector = null;
}
}
}
}
}
This gave me the following, when fleshed out with annotations and getters/setters:
//
// Here is the actual class, based on the above structure.
//
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class DMResponseMapper {
#JsonProperty("jobId")
private JobId jobId;
#JsonProperty("returncode")
private Integer returncode;
#JsonProperty("messageId")
private Object messageId;
#JsonProperty("arguments")
private Object arguments;
#JsonProperty("message")
private Object message;
#JsonProperty("jobId")
public JobId getJobId() {
return jobId;
}
#JsonProperty("jobId")
public void setJobId(JobId jobId) {
this.jobId = jobId;
}
#JsonProperty("returncode")
public Integer getReturncode() {
return returncode;
}
#JsonProperty("returncode")
public void setReturncode(Integer returncode) {
this.returncode = returncode;
}
#JsonProperty("messageId")
public Object getMessageId() {
return messageId;
}
#JsonProperty("messageId")
public void setMessageId(Object messageId) {
this.messageId = messageId;
}
#JsonProperty("arguments")
public Object getArguments() {
return arguments;
}
#JsonProperty("arguments")
public void setArguments(Object arguments) {
this.arguments = arguments;
}
#JsonProperty("message")
public Object getMessage() {
return message;
}
#JsonProperty("message")
public void setMessage(Object message) {
this.message = message;
}
public static class JobId {
#JsonProperty("EndpointReference")
private EndpointReference endpointReference;
#JsonProperty("EndpointReference")
public EndpointReference getEndpointReference() {
return endpointReference;
}
#JsonProperty("EndpointReference")
public void setEndpointReference(EndpointReference endpointReference) {
this.endpointReference = endpointReference;
}
public static class EndpointReference {
#JsonProperty("ReferenceParameters")
private ReferenceParameters referenceParameters;
#JsonProperty("Address")
private String address;
#JsonProperty("ReferenceParameters")
public ReferenceParameters getReferenceParameters() {
return referenceParameters;
}
#JsonProperty("ReferenceParameters")
public void setReferenceParameters(ReferenceParameters referenceParameters) {
this.referenceParameters = referenceParameters;
}
#JsonProperty("Address")
public String getAddress() {
return address;
}
#JsonProperty("Address")
public void setAddress(String address) {
this.address = address;
}
public static class ReferenceParameters {
#JsonProperty("ResourceURI")
private String resourceURI;
#JsonProperty("SelectorSet")
private SelectorSet selectorSet;
#JsonProperty("ResourceURI")
public String getResourceURI() {
return resourceURI;
}
#JsonProperty("ResourceURI")
public void setResourceURI(String resourceURI) {
this.resourceURI = resourceURI;
}
#JsonProperty("SelectorSet")
public SelectorSet getSelectorSet() {
return selectorSet;
}
#JsonProperty("SelectorSet")
public void setSelectorSet(SelectorSet selectorSet) {
this.selectorSet = selectorSet;
}
public static class SelectorSet {
#JsonProperty("Selector")
private List<String> selector = null;
#JsonProperty("Selector")
public List<String> getSelector() {
return selector;
}
#JsonProperty("Selector")
public void setSelector(List<String> selector) {
this.selector = selector;
}
}
}
}
}
}
This is invoked as follows:
First, some test data:
List<String> selector = new ArrayList();
selector.add("JID_502260561923");
selector.add("root/im");
Map<String, Object> selectorSet = new HashMap();
selectorSet.put("Selector", selector);
String resourceURI = "http://schemas.com/wbem/wscim/1/cim-schema/2/Job";
Map<String, Object> referenceParameters = new HashMap();
referenceParameters.put("ResourceURI", resourceURI);
referenceParameters.put("SelectorSet", selectorSet);
String address = "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous";
Map<String, Object> endpointReference = new HashMap();
endpointReference.put("ReferenceParameters", referenceParameters);
endpointReference.put("Address", address);
Map<String, Object> jobId = new HashMap();
jobId.put("EndpointReference", endpointReference);
Map<String, Object> inputMap = new HashMap();
inputMap.put("jobId", jobId);
inputMap.put("returncode", 4096);
inputMap.put("messageId", "foo");
inputMap.put("arguments", "bar");
inputMap.put("message", "baz");
Note I replaced your null values with strings, for testing and demonstration.
Then the code to perform the mapping:
ObjectMapper objectMapper = new ObjectMapper();
DMResponseMapper mapper = objectMapper.convertValue(inputMap, DMResponseMapper.class);
The resulting mapper object contains the test data:
Related
I try to serialize and deserialize an object DataSourceObject that wraps a Serializable object, but I don't know the type of the wrapped object.
When I deserialize the JSON, I get an exception:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.accor.assets.TestSerialization$DataSourceObject` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"#class":"com.accor.assets.TestSerialization$DataSourceObject","concreteObject":{"#class":"com.accor.assets.TestSerialization$HotelsListBean","version":"1.0","metaResponse":{"#class":"com.accor.assets.TestSerialization$MetaResponse","returncode":"0","date":"10/28/21 09:39:14 AM"},"hotelsList":{"#class":"com.accor.assets.TestSerialization$HotelsList","hotel":["java.util.ArrayList",[{"#class":"com.accor.assets.TestSerialization$Hotel","name":"My Hotel","code":"H001","nbstars":"4","countryCode":"G"[truncated 8 chars]; line: 1, column: 65]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1764)
at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1209)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1415)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:362)
Here is a complete example to reproduce the problem:
public class TestSerialization {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = objectMapper();
HotelsListBean hotelsListBean = new HotelsListBean();
hotelsListBean.setVersion("1.0");
MetaResponse metaResponse = new MetaResponse();
metaResponse.setReturncode("0");
metaResponse.setDate("10/28/21 09:39:14 AM");
hotelsListBean.setMetaResponse(metaResponse);
HotelsList hotelsList = new HotelsList();
Hotel hotel = new Hotel();
hotel.setCode("H001");
hotel.setName("My Hotel");
hotel.setCountryCode("GB");
hotel.setNbstars("4");
hotelsList.getHotel().add(hotel);
hotelsListBean.setHotelsList(hotelsList);
DataSourceObject<HotelsListBean> dataSourceObject = new DataSourceObject<>(hotelsListBean);
String json = objectMapper.writeValueAsString(dataSourceObject);
System.out.println(json);
Object result = objectMapper.readValue(json, Object.class);
System.out.println(result);
}
private static ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(JsonGenerator.Feature.IGNORE_UNKNOWN);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
// Register support of other new Java 8 datatypes outside of date/time: most notably Optional, OptionalLong, OptionalDouble
objectMapper.registerModule(new Jdk8Module());
// Register support for Java 8 date/time types (specified in JSR-310 specification)
objectMapper.registerModule(new JavaTimeModule());
return objectMapper;
}
public static class DataSourceObject<T extends Serializable> implements Serializable {
private static final long serialVersionUID = -6026669040755678830L;
private final T concreteObject;
public DataSourceObject(final T concreteObject) {
this.concreteObject = concreteObject;
}
public T getConcreteObject() {
return concreteObject;
}
}
public static class HotelsListBean implements Serializable {
private final static long serialVersionUID = 1L;
protected String version;
protected MetaResponse metaResponse;
protected HotelsList hotelsList;
public String getVersion() {
return version;
}
public void setVersion(String value) {
this.version = value;
}
public MetaResponse getMetaResponse() {
return metaResponse;
}
public void setMetaResponse(MetaResponse value) {
this.metaResponse = value;
}
public HotelsList getHotelsList() {
return hotelsList;
}
public void setHotelsList(HotelsList hotelsList) {
this.hotelsList = hotelsList;
}
}
public static class MetaResponse implements Serializable {
private final static long serialVersionUID = 1L;
protected String returncode;
protected String date;
public String getReturncode() {
return returncode;
}
public void setReturncode(String value) {
this.returncode = value;
}
public String getDate() {
return date;
}
public void setDate(String value) {
this.date = value;
}
}
public static class HotelsList implements Serializable {
private final static long serialVersionUID = 1L;
protected List<Hotel> hotel;
public List<Hotel> getHotel() {
if (hotel == null) {
hotel = new ArrayList<Hotel>();
}
return this.hotel;
}
}
public static class Hotel implements Serializable {
private final static long serialVersionUID = 1L;
protected String name;
protected String code;
protected String nbstars;
protected String countryCode;
public String getName() {
return name;
}
public void setName(String value) {
this.name = value;
}
public String getCode() {
return code;
}
public void setCode(String value) {
this.code = value;
}
public String getNbstars() {
return nbstars;
}
public void setNbstars(String value) {
this.nbstars = value;
}
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String value) {
this.countryCode = value;
}
}
}
I would like to know what is possible to successfully deserialize.
(I tried to add #JsonCreator on the DataSourceObject constructor but I get the same exception)
You need to have a no-args constructor for DataSourceObject. You have two options.
Make concreteObject not final:
public static class DataSourceObject<T extends Serializable> implements Serializable {
private static final long serialVersionUID = -6026669040755678830L;
private T concreteObject;
private DataSourceObject() { }
public DataSourceObject(final T concreteObject) {
this.concreteObject = concreteObject;
}
public T getConcreteObject() {
return concreteObject;
}
}
Keep concreteObject final, but it must be assigned to null in the no-args constructor:
public static class DataSourceObject<T extends Serializable> implements Serializable {
private static final long serialVersionUID = -6026669040755678830L;
private final T concreteObject;
private DataSourceObject() {
concreteObject = null;
}
public DataSourceObject(final T concreteObject) {
this.concreteObject = concreteObject;
}
public T getConcreteObject() {
return concreteObject;
}
}
Either option will work.
I have pojos with #JsonProperties. I use these to read JSON and parse to POJO. I am now having to post these pojos formatted as XML.
The Required XML format to successfully post looks like this (note the namespace type, xsi type formatting):
<network_objects>
<network_object xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="subnetNetworkObjectDTO">
<name>TestSubnet</name>
<display_name>TestSubnet</display_name>
<global>false</global>
<application_id>3</application_id>
<type>subnet</type>
<ip>5.207.206.0</ip>
<netmask>255.255.254.0</netmask>
</network_object>
<network_object xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="networkObjectGroupDTO">
<name>Test01Subnets</name>
<display_name>Test01Subnets</display_name>
<application_id>3</application_id>
<type>group</type>
</network_object>
</network_objects>
I pass the created Application Class (shown below) to convert to XML
ObjectMapper mapper = new XmlMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
byte[] val = mapper.writeValueAsBytes(myApp);
The output is a bit off and does not contain the xmlns and the xsi looks different. It also has 'Application' as root:
<Application>
<network_objects>
<network_object>
<network_object>
<#xsi.type>networkObjectGroupDTO</#xsi.type>
<name>name</name>
<display_name>displayName</display_name>
<application_id>3</application_id>
<type>group</type>
</network_object>
</network_object>
</network_objects>
</Application>
When I output the class to JSON, it looks as expected (No "Application" as root).
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
byte[] val = mapper.writeValueAsBytes(myApp);
{
"network_objects" : {
"network_object" : [ {
"#xsi.type" : "networkObjectGroupDTO",
"name" : "name",
"display_name" : "displayName",
"application_id" : 3,
"type" : "group"
}
}
}
What do I need to modify with my XmlMapper() or POJOs in order to get the XML formatted correctly?
Below are the POJOs used for this.
Application Class:
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"network_objects"
})
public class Application {
#JsonProperty("network_objects")
private NetworkObjects networkObjects;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("network_objects")
public NetworkObjects getNetworkObjects() {
return networkObjects;
}
#JsonProperty("network_objects")
public void setNetworkObjects(NetworkObjects networkObjects) {
this.networkObjects = networkObjects;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
NetworkObjects Class:
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"network_object"
})
public class NetworkObjects {
#JsonProperty("network_object")
private List<NetworkObject> networkObject = null;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("network_object")
public List<NetworkObject> getNetworkObject() {
return networkObject;
}
#JsonProperty("network_object")
public void setNetworkObject(List<NetworkObject> networkObject) {
this.networkObject = networkObject;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
NetworkObject Class:
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"#xsi.type",
"id",
"uid",
"name",
"display_name",
"global",
"comment",
"application_id",
"type",
"ip",
"access_allowed",
"member",
"last_ip",
"first_ip",
"netmask"
})
public class NetworkObject {
#JsonProperty("#xsi.type")
private String xsiType;
#JsonProperty("id")
private Integer id;
#JsonProperty("uid")
private String uid;
#JsonProperty("name")
private String name;
#JsonProperty("display_name")
private String displayName;
#JsonProperty("global")
private Boolean global;
#JsonProperty("comment")
private String comment;
#JsonProperty("application_id")
private Integer applicationId;
#JsonProperty("type")
private String type;
#JsonProperty("ip")
private String ip;
#JsonProperty("access_allowed")
private Boolean accessAllowed;
#JsonProperty("member")
private List<Member> member = null;
#JsonProperty("last_ip")
private String lastIp;
#JsonProperty("first_ip")
private String firstIp;
#JsonProperty("netmask")
private String netmask;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("#xsi.type")
public String getXsiType() {
return xsiType;
}
#JsonProperty("#xsi.type")
public void setXsiType(String xsiType) {
this.xsiType = xsiType;
}
#JsonProperty("id")
public Integer getId() {
return id;
}
#JsonProperty("id")
public void setId(Integer id) {
this.id = id;
}
#JsonProperty("uid")
public String getUid() {
return uid;
}
#JsonProperty("uid")
public void setUid(String uid) {
this.uid = uid;
}
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
#JsonProperty("display_name")
public String getDisplayName() {
return displayName;
}
#JsonProperty("display_name")
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
#JsonProperty("global")
public Boolean getGlobal() {
return global;
}
#JsonProperty("global")
public void setGlobal(Boolean global) {
this.global = global;
}
#JsonProperty("comment")
public String getComment() {
return comment;
}
#JsonProperty("comment")
public void setComment(String comment) {
this.comment = comment;
}
#JsonProperty("application_id")
public Integer getApplicationId() {
return applicationId;
}
#JsonProperty("application_id")
public void setApplicationId(Integer applicationId) {
this.applicationId = applicationId;
}
#JsonProperty("type")
public String getType() {
return type;
}
#JsonProperty("type")
public void setType(String type) {
this.type = type;
}
#JsonProperty("ip")
public String getIp() {
return ip;
}
#JsonProperty("ip")
public void setIp(String ip) {
this.ip = ip;
}
#JsonProperty("access_allowed")
public Boolean getAccessAllowed() {
return accessAllowed;
}
#JsonProperty("access_allowed")
public void setAccessAllowed(Boolean accessAllowed) {
this.accessAllowed = accessAllowed;
}
#JsonProperty("member")
public List<Member> getMember() {
return member;
}
#JsonProperty("member")
public void setMember(List<Member> member) {
this.member = member;
}
#JsonProperty("last_ip")
public String getLastIp() {
return lastIp;
}
#JsonProperty("last_ip")
public void setLastIp(String lastIp) {
this.lastIp = lastIp;
}
#JsonProperty("first_ip")
public String getFirstIp() {
return firstIp;
}
#JsonProperty("first_ip")
public void setFirstIp(String firstIp) {
this.firstIp = firstIp;
}
#JsonProperty("netmask")
public String getNetmask() {
return netmask;
}
#JsonProperty("netmask")
public void setNetmask(String netmask) {
this.netmask = netmask;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
I used your bean classes and json string. Almost everything is possible with the conversion, but there are some issues. I got this so far, if someone can derive the final solution it will be great.
Application.java
Not really any changes.
NetworkObject.java
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({"#xsi.type", "id", "uid", "name", "display_name", "global", "comment", "application_id", "type", "ip", "access_allowed", "member", "last_ip", "first_ip", "netmask"})
public class NetworkObject {
#JsonProperty("id")
private Integer id;
#JsonProperty("uid")
private String uid;
#JsonProperty("name")
private String name;
#JsonProperty("display_name")
private String displayName;
#JsonProperty("global")
private Boolean global;
#JsonProperty("comment")
private String comment;
#JsonProperty("application_id")
private Integer applicationId;
#JsonProperty("type")
private String type;
#JsonProperty("#xsi.type")
// CHANGE: You cannot have same element name and attribute name, so I had to change this to xtype if someone knows how to tackle this, that will be final answer
#JacksonXmlProperty(localName = "xtype", isAttribute = true, namespace = "http://www.w3.org/2001/XMLSchema-instance")
private String xsiType;
#JsonProperty("ip")
private String ip;
#JsonProperty("access_allowed")
private Boolean accessAllowed;
#JsonProperty("member")
private List<Member> member = null;
#JsonProperty("last_ip")
private String lastIp;
#JsonProperty("first_ip")
private String firstIp;
#JsonProperty("netmask")
private String netmask;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
// CHANGE: We don't really need on getter setters #JsonProperty("#xsi.type")
public String getXsiType() {
return xsiType;
}
// CHANGE: We don't really need on getter setters #JsonProperty("#xsi.type")
public void setXsiType(String xsiType) {
this.xsiType = xsiType;
}
#JsonProperty("id")
public Integer getId() {
return id;
}
#JsonProperty("id")
public void setId(Integer id) {
this.id = id;
}
#JsonProperty("uid")
public String getUid() {
return uid;
}
#JsonProperty("uid")
public void setUid(String uid) {
this.uid = uid;
}
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
#JsonProperty("display_name")
public String getDisplayName() {
return displayName;
}
#JsonProperty("display_name")
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
#JsonProperty("global")
public Boolean getGlobal() {
return global;
}
#JsonProperty("global")
public void setGlobal(Boolean global) {
this.global = global;
}
#JsonProperty("comment")
public String getComment() {
return comment;
}
#JsonProperty("comment")
public void setComment(String comment) {
this.comment = comment;
}
#JsonProperty("application_id")
public Integer getApplicationId() {
return applicationId;
}
#JsonProperty("application_id")
public void setApplicationId(Integer applicationId) {
this.applicationId = applicationId;
}
#JsonProperty("type")
public String getType() {
return type;
}
#JsonProperty("type")
public void setType(String type) {
this.type = type;
}
#JsonProperty("ip")
public String getIp() {
return ip;
}
#JsonProperty("ip")
public void setIp(String ip) {
this.ip = ip;
}
#JsonProperty("access_allowed")
public Boolean getAccessAllowed() {
return accessAllowed;
}
#JsonProperty("access_allowed")
public void setAccessAllowed(Boolean accessAllowed) {
this.accessAllowed = accessAllowed;
}
#JsonProperty("member")
public List<Member> getMember() {
return member;
}
#JsonProperty("member")
public void setMember(List<Member> member) {
this.member = member;
}
#JsonProperty("last_ip")
public String getLastIp() {
return lastIp;
}
#JsonProperty("last_ip")
public void setLastIp(String lastIp) {
this.lastIp = lastIp;
}
#JsonProperty("first_ip")
public String getFirstIp() {
return firstIp;
}
#JsonProperty("first_ip")
public void setFirstIp(String firstIp) {
this.firstIp = firstIp;
}
#JsonProperty("netmask")
public String getNetmask() {
return netmask;
}
#JsonProperty("netmask")
public void setNetmask(String netmask) {
this.netmask = netmask;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
NetworkObjects
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({"network_object"})
// CHANGE: To provide root element name
#JacksonXmlRootElement(localName = "network_objects")
public class NetworkObjects {
#JsonProperty("network_object")
#JacksonXmlElementWrapper(useWrapping = false)
// CHANGE: To ignore <network_object><network_object></network_object></network_object>
private List<NetworkObject> networkObject = null;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("network_object")
public List<NetworkObject> getNetworkObject() {
return networkObject;
}
#JsonProperty("network_object")
public void setNetworkObject(List<NetworkObject> networkObject) {
this.networkObject = networkObject;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
Main.java
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper jsonOM = new ObjectMapper();
String jsomn = Files.readAllLines(Paths.get("sample.json"), StandardCharsets.US_ASCII).stream().collect(Collectors.joining(""));
Application myApp = jsonOM.readValue(jsomn, Application.class);
ObjectMapper mapper = new XmlMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
// Not writing entire object
System.out.println(mapper.writer().writeValueAsString(myApp.getNetworkObjects()));
}
OUPUT
<network_objects>
<network_object xmlns:wstxns1="http://www.w3.org/2001/XMLSchema-instance" wstxns1:xtype="networkObjectGroupDTO">
<name>name</name>
<display_name>displayName</display_name>
<application_id>3</application_id>
<type>group</type>
</network_object>
</network_objects>
You can use this:
XMLInputFactory inputFactory = XMLInputFactory.newFactory();
inputFactory.setProperty("javax.xml.stream.isNamespaceAware", false);
XmlMapper xmlMapper = new XmlMapper(inputFactory);
We are using Spring Boot to expose a REST endpoint which is called by a dumb client which delivers us the following:
{
"timestamp": "2016-08-16T14:30.000Z",
"data": "{\"amount\":1,\"product\":\"BASIC PRODUCT\"}"
}
We've created the following objects:
#JsonDeserialize(builder = Message.Builder.class)
public final class Message {
private final String timestamp;
private final Data data;
public String getTimestamp() {...}
public Data getData() {...}
#JsonPOJOBuilder
public static final class Builder {
private String timestamp;
private Data data;
public Builder withTimestamp(final String timestamp) {...}
public Builder withData(final Data data) {...}
}
}
and
#JsonDeserialize(builder = Data.Builder.class)
public final class Data {
private final String product;
private final int amount;
public String getProduct() {...}
public int getAmount() {...}
#JsonPOJOBuilder
public static final class Builder {
private String product;
private int amount;
public Builder withProduct(final String product) {...}
public Builder withAmount(final int amount) {...}
}
}
and exposed the endpoint as
#RequestMapping(consumes = "application/json", method = POST)
public ResponseEntity<?> receive(#RequestBody Message message) {
/// ...
}
but control doesn't even reach the receive method and fails with 400 BAD REQUEST. I believe this has to do with the fact that data is a JSON-valued string. Does Jackson provide any annotation that I can use to force the JSON-valued string to be deserialized as an instance of Data?
The key is in public Builder withData() method of Message.Builder.class to explicitly parse JSON-valued string to Data type. Change the method parameter to String instead of Data and call ObjectMapper().readValue(JSON-valued string, Data.class) to deserialize it into Data.
For example like this:
public Builder withData(final String jsonValue) throws JsonParseException, JsonMappingException, IOException {
Data data = new ObjectMapper().readValue(jsonValue, Data.class);
this.data = data;
return this;
}
For the clarity sake here you are my whole POJOs:
Message:
public final class Message {
private final String timestamp;
private final Data data;
private Message(Builder builder){
this.timestamp = builder.timestamp;
this.data = builder.data;
}
public String getTimestamp() {...}
public Data getData() {...}
#JsonPOJOBuilder
public static final class Builder {
private String timestamp;
private Data data;
private static ObjectMapper mapper = new ObjectMapper();
public Builder withTimestamp(final String timestamp) {
this.timestamp = timestamp;
return this;
}
public Builder withData(final String jsonValue) throws JsonParseException, JsonMappingException, IOException {
Data data = mapper.readValue(jsonValue, Data.class);
this.data = data;
return this;
}
public Message build() {
return new Message(this);
}
} // Builder
}
Data:
public final class Data {
private final String product;
private final int amount;
private Data(Builder builder){
this.product = builder.product;
this.amount = builder.amount;
}
public String getProduct() {...}
public int getAmount() {...}
#JsonPOJOBuilder
public static final class Builder {
private String product;
private int amount;
public Builder withProduct(final String product) {
this.product = product;
return this;
}
public Builder withAmount(final int amount) {
this.amount = amount;
return this;
}
public Data build() {
return new Data(this);
}
} // Builder
}
Hope it helps.
I need to create a Map from java bean such that the key is prefixed with name of the java bean variable. I am using jackson for this. Example given below:
public class Address{
String city;
String state;
//setters and getters
}
Address address = new Address();
address.setCity("myCity");
address.setState("myState");
I am creating map using following:
ObjectMapper objectMapper = new ObjectMapper();
Map map = objectMapper.convertValue(address, HashMap.class);
Which gives me following output:
{"city":"myCity", "state":"myState"}
I need to add class variable name to the key as shown below:
{"address.city":"myCity", "address.state":"myState"}
How do I achieve that?
If you have jackson-annotations enabled:
public class Address{
#JsonProperty("address.city")
String city;
#JsonProperty("address.state")
String state;
//setters and getters
}
read more about it here: https://github.com/FasterXML/jackson-annotations
It is possible to customise bean serialization by registering a BeanSerializerModifier. This specifically supports renaming properties by applying a NameTransformer to each BeanPropertyWriter.
#Test
public void prepend_class_name_to_property_keys() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Function<Class<?>, String> classPrefix = clazz -> clazz.getSimpleName().toLowerCase() + ".";
mapper.registerModule(new Module() {
#Override
public String getModuleName() {
return "Example";
}
#Override
public Version version() {
return Version.unknownVersion();
}
#Override
public void setupModule(SetupContext context) {
context.addBeanSerializerModifier(new BeanSerializerModifier() {
#Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
String prefix = classPrefix.apply(beanDesc.getBeanClass());
return beanProperties.stream().map(prop -> prop.rename(new NameTransformer() {
#Override
public String transform(String name) {
return prefix + name;
}
#Override
public String reverse(String transformed) {
return transformed.substring(prefix.length());
}
})).collect(toList());
}
});
}
});
assertThat(mapper.writeValueAsString(new Address("somewhere", "someplace")),
equivalentTo("{ 'address.line1' : 'somewhere', 'address.line2' : 'someplace'}"));
}
public static final class Address {
public final String line1;
public final String line2;
public Address(String line1, String line2) {
this.line1 = line1;
this.line2 = line2;
}
}
In a personal project i have the following xml structures to make my own java classes:
first:
<response>
<action>getcredits</action>
<data>
<account>
<username>aptalaser</username>
<balance>193</balance>
</account>
</data>
second:
<response>
<action>getMsgInfoByID</action>
<data>
<messageid>c36d7ee5-16f9-4193-9a75-0537e590e9d3</messageid>
<originator>+17036231081 [4]</originator>
<recipient>10958</recipient>
<folder>INBOX</folder>
<senttime>2011.10.17 13:10:26</senttime>
<receivedtime>2011.10.17 13:10:26</receivedtime>
<creationtime>2011.10.17 13:10:26</creationtime>
<callbackid/>
<state>0</state>
<operatornames>
<operatorname>SMPP0</operatorname>
</operatornames>
<routes>
<route>defin_admin</route>
</routes>
<optionalfields>
<optionalfield name="30" value="35333131572D31303133322D303530364E2D333434544600"/>
<optionalfield name="8528" value="017F"/>
</optionalfields>
<messagetype>SMS:TEXT</messagetype>
<messagedata>Test message</messagedata>
</data>
third:
<response>
<action>sendmessage</action>
<data>
<acceptreport>
<statuscode>0</statuscode>
<statusmessage>Message accepted for delivery</statusmessage>
<messageid>0f06bbd9-0894-4fb4-9c4b-68e29363d299</messageid>
<originator>aptalaser</originator>
<recipient>8588430260</recipient>
<messagetype>SMS:TEXT</messagetype>
<messagedata>Bom dia cara.</messagedata>
</acceptreport>
</data>
The structures are divided in two places: a action to indicating the method acessed and the data section: a generic portion with will change the content dinamically in response to access a different method.
Following this idea i decide create a Response class with two field: a String action and a field Data:
The Response class:
/* omite desnecessary imports */
#XmlRootElement(name = "response")
#XmlAccessorType(XmlAccessType.PROPERTY)
public class Response {
private String action;
private Data data;
public String getAction() {
return action;
}
/* omitted set methds */
#XmlElementRef
public Data getData() {
return data;
}
#Override
public String toString() {
String template = "( action: %s, data: %s )";
return String.format(template, this.action, this.data);
}
}
The Data class, the base class for all Data sections
#XmlSeeAlso({ GetInfoMessageData.class, GetAccountData.class, SendMessageData.class })
public class Data {
}
The GetAccountClass to represent account retrieve information
/* omite desnecessary imports */
#XmlRootElement(name = "data")
#XmlAccessorType(XmlAccessType.PROPERTY)
public class GetAccountData extends Data {
private List<Account> account;
public List<Account> getAccount() {
return account;
}
/* omitted set methos */
#Override
public String toString() {
return String.format("Account( %s )", this.account);
}
public static class Account {
private String username;
private Long balance;
public String getUsername() {
return username;
}
public Long getBalance() {
return balance;
}
/* omitted set methods */
#Override
public String toString() {
return String.format("[ usr: %s, credit: %d ]", this.username, this.balance);
}
}
}
The class represents the message data
/* omite desnecessary imports */
#XmlRootElement(name = "data")
#XmlAccessorType(XmlAccessType.PROPERTY)
public class GetInfoMessageData extends Data {
private String messageId;
private String destino;
private String recipiente;
private String folder;
private Date dataCricao;
private Date dataEnvio;
private Date dataRecebimento;
private Integer status;
private String tipoMensagem;
private String mensagem;
private List<Protocolo> protocolos;
private List<Route> rotas;
private List<Field> optionalFields;
private Error error;
#XmlAccessorType(XmlAccessType.PROPERTY)
public static class Protocolo {
private String nomeProtocolo;
#XmlElement(name = "operatorname", defaultValue = "")
public String getNomeProtocolo() {
return nomeProtocolo;
}
/* omitted set methods */
}
#XmlAccessorType(XmlAccessType.PROPERTY)
public static class Error {
private String errorMessage;
#XmlElement(name = "errormessage")
public String getErrorMessage() {
return errorMessage;
}
/* omitted set methods */
}
#XmlAccessorType(XmlAccessType.PROPERTY)
public static class Route {
private String route;
#XmlElement(defaultValue = "")
public String getRoute() {
return route;
}
/* omitted set methods */
}
#XmlAccessorType(XmlAccessType.PROPERTY)
public static class Field {
private String name;
private String value;
#XmlAttribute
public String getName() {
return name;
}
#XmlAttribute
public String getValue() {
return value;
}
/* omitted set methods */
}
#XmlElement(name = "messageid", required = true)
public final String getMessageId() {
return messageId;
}
#XmlElement(name = "originator", required = true)
public final String getDestino() {
return destino;
}
#XmlElement(name = "recipient", defaultValue = "")
public final String getRecipiente() {
return recipiente;
}
#XmlElement(name = "folder", defaultValue = "")
public final String getFolder() {
return folder;
}
#XmlElement(name = "creationtime")
#XmlJavaTypeAdapter(type = Date.class, value = JavaDateAdapter.class)
public final Date getDataCricao() {
return dataCricao;
}
#XmlElement(name = "senttime")
#XmlJavaTypeAdapter(type = Date.class, value = JavaDateAdapter.class)
public final Date getDataEnvio() {
return dataEnvio;
}
#XmlElement(name = "receivedtime")
#XmlJavaTypeAdapter(type = Date.class, value = JavaDateAdapter.class)
public final Date getDataRecebimento() {
return dataRecebimento;
}
#XmlElement(name = "state", required = true)
public final Integer getStatus() {
return status;
}
#XmlElement(name = "messagetype", required = true)
public final String getTipoMensagem() {
return tipoMensagem;
}
#XmlElement(name = "messagedata")
public final String getMensagem() {
return mensagem;
}
#XmlElement(name = "operatornames")
public final List<Protocolo> getProtocolos() {
return protocolos;
}
#XmlElement(name = "routes")
public final List<Route> getRotas() {
return rotas;
}
#XmlElement(name = "optionalfield")
#XmlElementWrapper(name = "optionalfields")
public List<Field> getOptionalFields() {
return optionalFields;
}
#XmlElement(name = "error")
public Error getError() {
return error;
}
/* omitted set methods */
}
The class represent the sendMessage response operation
/* omite desnecessary imports */
#XmlRootElement(name = "data")
#XmlAccessorType(XmlAccessType.PROPERTY)
public class SendMessageData extends Data {
private AcceptReport acceptReport;
#XmlElement(name = "acceptreport")
public AcceptReport getAcceptReport() {
return acceptReport;
}
#SuppressWarnings("unused")
public void setAcceptReport(AcceptReport acceptReport) {
this.acceptReport = acceptReport;
}
#Override
public String toString() {
return String.format("Report( %s )", this.acceptReport);
}
#XmlRootElement(name = "acceptreport")
public static class AcceptReport {
private Integer status;
private String statusMessage;
private String messageId;
private String originator;
private String recipient;
private String messageType;
private String messageData;
#XmlElement(name = "statuscode")
public Integer getStatus() {
return status;
}
#XmlElement(name = "statusmessage")
public String getStatusMessage() {
return statusMessage;
}
#XmlElement(name = "messageid")
public String getMessageId() {
return messageId;
}
#XmlElement(name = "originator")
public String getOriginator() {
return originator;
}
#XmlElement(name = "recipient")
public String getRecipient() {
return recipient;
}
#XmlElement(name = "messagetype")
public String getMessageType() {
return messageType;
}
#XmlElement(name = "messagedata")
public String getMessageData() {
return messageData;
}
/* set methods omited */
#Override
public String toString() {
return String.format("[ stats: %d, msgId: %s, msg: %s ]", this.status, this.messageId, this.messageData);
}
}
}
The xml 'data' section don't have anything to identifing 'what son of Data will be use in the ummarshaling operation?'
My test works fine in the marshalling operation but in the ummarshalling its broken because the jaxb don't identify the son of Data to use.
/* omite desnecessary imports */
public class Teste {
public static void main(String[] args) throws JAXBException {
JAXBContext ctx = JAXBContext.newInstance(
Data.class, GetAccountData.class,
GetInfoMessageData.class, Response.class, SendMessageData.class
);
Marshaller marshaller = ctx.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Response r = new Response();
r.setAction("getcredits");
GetAccountData data = new GetAccountData();
Account account = new GetAccountData.Account();
account.setUsername("aptalaser");
account.setBalance(12523L);
data.setAccount(Arrays.asList(account));
r.setData(data);
//in there is equal to first xml
marshaller.marshal(r, System.out);
Unmarshaller unmarshaller = ctx.createUnmarshaller();
Response resp = (Response) unmarshaller.unmarshal(new File("./get-credits.xml"));
//but in there the resp retrieved is to the third xml(and the Data field is null)
System.out.println(resp);
}
}
The question is: I need implement my own customized converter or i can make that with simple configurations like annotations?
Thanks for help.
For the purposes of unmarshalling you need to have a unique identifier that can be used to identify the appropriate subclass to instantiate.
#XmlElementRef
Since you are using #XmlElementRef this unique identifier should be the #XmlRootElement of the subclass, but you currently have all subclasses with #XmlRootElement(name="data").
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html
xsi:type Attribute
If you want to always leverage the data element then you need something else as the inheritance indicator. If you remove the #XmlElementRef annotation then JAXB will leverage the xsi:type attribute for this purpose.
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html
Element Content
If neither of the above will work for you then you can leverage an XmlAdapter.
http://blog.bdoughan.com/2012/01/jaxb-and-inhertiance-using-xmladapter.html
UPDATE
Thank you #Blaise this help me so much, one more thing: i need process
this model, so i need implement different response class with
appropriate subclass field?
If you know which type of response you are receiving this is a valid approach. Then when you do the unmarshal you can specify the class you are unmarshalling. You will need to do this because the response root element corresponds to all the response classes you will make.
GetAccountDataResponse = unmarshaller.unmarshal(xml, GetAccountDataResponse.class).getValue();
If you don't know what type of response you are receiving and need to decide based on the content of the message then an XmlAdapter approach for handling inheritance will work.
http://blog.bdoughan.com/2012/01/jaxb-and-inhertiance-using-xmladapter.html