Jackson: deserialize custom attributes in XML to POJO - java

I would like to deserialize and map to class following values by name attribute.
This is piece of my XML file.
<custom-attributes>
<custom-attribute name="Name1" dt:dt="string">VALUE</custom-attribute>
<custom-attribute name="Name2" dt:dt="string">
<value>1111</value>
<value>1111</value>
<value>1111</value>
</custom-attribute>
<custom-attribute name="Name3" dt:dt="string">VALUE2</custom-attribute>
<custom-attribute dt:dt="boolean" name="Name3">VALUE3</custom-attribute>
<custom-attribute dt:dt="boolean" name="Name4">VALUE4</custom-attribute>
</custom-attributes>
And This is piece of my pojo class
#JsonIgnoreProperties(ignoreUnknown = true)
public class CustomAttributes {
#JacksonXmlProperty(localName="name3", isAttribute = true)
private String roleID;
public String getRoleID() {
return roleID;
}
public void setRoleID(String roleID) {
this.roleID = roleID;
}
}
Do you know ho to properly read values from those attribues by name ? Currently im receiving null

I am not sure what the result is supposed to look like, but if you want
to parse the complete xml into matching objects they would look like this:
public class CustomAttributeList {
#JacksonXmlProperty(localName = "custom-attributes")
private List<CustomAttributes> list;
...
}
#JacksonXmlRootElement(localName = "custom-attribute")
public class CustomAttributes {
// the name attribute
#JacksonXmlProperty
private String name;
// the datatype from the dt:dt field
#JacksonXmlProperty(namespace = "dt")
private String dt;
// the content between the tags (if available)
#JacksonXmlText
private String content;
// the values in the content (if available)
#JacksonXmlProperty(localName = "value")
#JacksonXmlElementWrapper(useWrapping = false)
private List<String> values;
...
}
Note that the localName="name3" from your question is not referring to a property at all.

Related

Java object not populated from json request for inner class

Have searched in different sites but couldn't find correct answer, hence posting this request though it could possible duplicates.sorry for that.
I am sending the below json request to my back-end service and converting to java object for processing. I can see the request body passed to my service but when i convert from json to java object , values are not populating
{
"data":{
"username":"martin",
"customerId":1234567890,
"firstName":"john",
"lastName":"smith",
"password":"p#ssrr0rd##12",
"email":"john.smith#gmail.com",
"contactNumber":"0342323443",
"department":"sports",
"location":"texas",
"status":"unlocked",
"OrderConfigs":[
{
"vpnId":"N4234554R",
"serviceId":"connectNow",
"serviceType":"WRLIP",
"ipAddress":"10.101.10.3",
"fRoute":[
"10.255.253.0/30",
" 10.255.254.0/30"
],
"timeout":1800,
"mapId":"test_map"
}
]
}
}
My Parser class have something like,
JSONObject requestJSON = new JSONObject(requestBody).getJSONObject("data");
ObjectMapper mapper = new ObjectMapper();
final String jsonData = requestJSON.toString();
OrderDTO mappedObject= mapper.readValue(jsonData , OrderDTO .class);
// I can see value coming from front-end but not populating in the mappedObject
My OrderDTO.java
#JsonInclude(value = Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true,value = {"hibernateLazyInitializer", "handler", "created"})
public class OrderDTO {
private String username;
private long customerId;
private String source;
private String firstName;
private String lastName;
private String email;
private String contactNumber;
private String password;
private String department;
private String location;
private String status;
private List<OrderConfig> OrderConfigs;
#JsonInclude(value = Include.NON_NULL)
public class OrderConfig {
private String vpnId;
private String serviceId;
private String serviceType;
private String ipAddress;
private String mapId;
private String[] fRoutes;
private Map<String, Object> attributes;
private SubConfig subConfig;
private String routeFlag;
getter/setters
.....
}
all setter/getter
}
Not sure what I'm missing here. Is this right way to do?
If your are trying to use inner class, correct way to use is to declare it static for Jackson to work with inner classes.
For reference check this
code changes made are
#JsonInclude(value = Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
static class OrderConfig {
Make sure that your json tag names match with variable names of java object
Ex : "fRoute":[
"10.255.253.0/30",
" 10.255.254.0/30"
],
private String[] fRoutes;
OrderConfigs fields will not be initialized, just modify your bean as
#JsonProperty("OrderConfigs")
private List<OrderConfig> orderConfigs;
// setter and getter as setOrderConfigs / getOrderConfigs
See my answer here. (same issue)

Missing name, in state: START_OBJECT parsing XML using Jackson

I'm trying to parse some XML that looks like this:
<correlationMatrix>
<assetMatrix numAssets="45">
<correlations asset="Name1" />
<correlations asset="Name2">
<correlation asset="Name3">1.23</correlation>
</correlations>
<correlations asset="Name4">
<correlation asset="Name5">2.34</correlation>
<correlation asset="Name6">3.45</correlation>
</correlations>
</assetMatrix>
</correlationMatrix>
I've created 3 classes:
#JsonIgnoreProperties(ignoreUnknown = true)
public class CorrelationMatrix {
private List<Correlations> assetMatrix;
public List<Correlations> getAssetMatrix() {
return assetMatrix;
}
public void setAssetMatrix(List<Correlations> assetMatrix) {
this.assetMatrix = assetMatrix;
}
}
And
#JsonIgnoreProperties(ignoreUnknown = true)
public class Correlations {
private String asset;
private List<Correlation> correlation;
public String getAsset() {
return asset;
}
public void setAsset(String asset) {
this.asset = asset;
}
public List<Correlation> getCorrelation() {
return correlation;
}
public void setCorrelations(List<Correlation> correlation) {
this.correlation = correlation;
}
}
Then finally
#JsonIgnoreProperties(ignoreUnknown = true)
public class Correlation {
}
As you can see I've removed everything from the final inner class, but it still fails to parse. I've tried removing <correlations asset="Name1" /> from the input but that's not the source of the problem. If I remove private List<Correlation> correlation; from Correlations then that does then parse successfully but obviously doesn't have the information I need.
What is it that I need to do differently here to parse what is essentially a 2 dimensional array from XML into Java using Jackson (2.2.0 if that matters)?
The error I get is:
Missing name, in state: START_OBJECT (through reference chain: CorrelationMatrix["assetMatrix"]->Correlations["correlation"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(
Update:
The problem seems to be associated with the values inside correlation. If I remove 1.23, 2.34 and 3.45 from my example data then it parses - so I need to somehow tell Jackson how to map them.
I was able to parse all the elements in the example xml with these modified classes (add getters, setters and use correct name setCorrelation in Correlations):
class CorrelationMatrix {
private AssetMatrix assetMatrix;
}
class AssetMatrix {
#JacksonXmlProperty(isAttribute = true)
private int numAssets;
#JacksonXmlElementWrapper(useWrapping = false)
private List<Correlations> correlations;
}
class Correlations {
#JacksonXmlProperty(isAttribute = true)
private String asset;
#JacksonXmlElementWrapper(useWrapping = false)
private List<Correlation> correlation;
}
class Correlation {
#JacksonXmlProperty(isAttribute = true)
private String asset;
#JacksonXmlText
private double correlation;
}
I didn't need #JsonIgnoreProperties(ignoreUnknown = true) anywhere
#JacksonXmlProperty(isAttribute = true) is needed for attributes like asset and numAssets
There are 2 types of lists in the xml that are both unwrapped so specify it with this #JacksonXmlElementWrapper(useWrapping = false)
You can parse the innermost double numbers with this #JacksonXmlText although the field in Java is not text.
I introduced a wrapper class AssetMatrix to capture numAssets

How to collect all xml elements in a list?

I am using jackson-dataformat-xml.
I have the following classes:
public class CTHotel {
#JacksonXmlProperty(localName = "basic-info")
private HotelBaseInfo hotelBaseInfo;
//other properties and getters and setters
}
public class HotelBaseInfo {
#JacksonXmlProperty(localName = "hotel-name")
private String hotelName;
#JacksonXmlElementWrapper(localName = "hotel-amenities")
private List<HotelAmenity> hotelAmenities;
//other properties and getters/setters
}
public class HotelAmenity {
private String category;
private String description;
#JacksonXmlElementWrapper(localName = "amenities")
private List<String> amenities;
//other properties and getters/setters
}
My XML is this:
<hotels>
<hotel>
<basic-info>
<hotel-name>Hotel XYZ</hotel-name>
<hotel-amenities>
<hotel-amenity>
<category>F&B</category>
<description>Random Text</description>
<amenities>
<amenity>Cafe</amenity>
<amenity>Bar</amenity>
<amenity>Rastaurant</amenity>
</amenities>
</hotel-amenity>
<hotel-amenity>
...
</hotel-amenity>
</hotel-amenities>
</basic-info>
</hotel>
<hotel>
...
</hotel>
</hotels>
My question is, how can I map amenities as list of strings in my HotelAmenity class as mentioned above ? What annotation should I use on amenities field ?
#JacksonXmlElementWrapper annotation on hotelAmenities field of Hotel class is working just fine.
I get the below error while mapping :
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: java.io.StringReader#507bcc81; line: 3, column: 1039] (through reference chain: com.example.response.HotelSearchResponse["hotels"]->java.util.ArrayList[2]->com.example.response.CTHotel["basic-info"]->com.example.response.HotelBaseInfo["hotel-amenities"]->java.util.ArrayList[1]->com.example.response.HotelAmenity["amenities"]->java.util.ArrayList[9])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148) ~[jackson-databind-2.6.5.jar:2.6.5]
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:857) ~[jackson-databind-2.6.5.jar:2.6.5]
...
Here's the code, that I hope would answer your question:
/**Class Hotels*/
#JacksonXmlRootElement(localName = "hotels")
public class Hotels {
#JacksonXmlElementWrapper(useWrapping = false)
private List<Hotel> hotel;
//Other getters and setters
}
/**Class Hotel*/
#JacksonXmlRootElement(localName = "hotel")
public class Hotel {
#JacksonXmlProperty(localName = "basic-info")
private HotelBaseInfo hotelBaseInfo;
//Other getters and setters
}
/**Class HotelBaseInfo*/
public class HotelBaseInfo {
#JacksonXmlProperty(localName = "hotel-name")
private String hotelName;
#JacksonXmlElementWrapper(localName = "hotel-amenities")
private List<HotelAmenity> hotelAmenities;
//Other getters and setters
}
/**Class HotelAmenity*/
public class HotelAmenity {
private String category;
private String description;
#JacksonXmlElementWrapper(localName = "amenities")
private List<Amenities> amenity;
static class Amenities {
#JacksonXmlText
private String value;
}
//Other getters and setters
}
Here's what worked for me:
public class JacksonXmlParsing {
#JacksonXmlRootElement(localName = "hotels")
static class HotelSearchResponse {
#JacksonXmlElementWrapper(localName = "hotel")
private List<CTHotel> hotels;
//other properties and getters and setters
}
static class CTHotel {
#JacksonXmlProperty(localName = "hotel-name")
private String hotelName;
#JacksonXmlElementWrapper(localName = "hotel-amenities")
private List<HotelAmenity> hotelAmenities;
//other properties and getters and setters
}
static class HotelAmenity {
private String category;
private String description;
#JacksonXmlElementWrapper
private List<String> amenities;
//other properties and getters/setters
}
public static void main(String[] args) throws IOException {
XmlMapper xmlMapper = new XmlMapper();
File file = new File("./src/main/resources/file.xml");
HotelSearchResponse response = xmlMapper.readValue(file, HotelSearchResponse.class);
System.out.println(response);
}
}
Output:
HotelSearchResponse(hotels=[CTHotel(hotelName=Hotel XYZ, hotelAmenities=[HotelAmenity(category=F&B, description=Random Text, amenities=[Cafe, Bar, Rastaurant])])])
But basic-info tag is missed, I could find out why.

SimpleXMLConverter parse xml nodes

I'm stuck. How to parse Node with same name child node?
In this example i need nodes with rate attribute.
<xml>
<Rates>
<Rates winrate_cap="9999">
<Rates rate="323"/>
<Rates rate="343"/>
<Rates rate="2338"/>
<Rates rate="233"/>
</Rates>
</Rates>
</xml>
My response wrapper class:
#Root(name = "xml", strict = false)
public class XMLResponse {
#ElementList(entry = "Rates")
public List<Rates> response;
public static class Rates {
#Attribute(name = "winrate_cap", required = false)
public String winrate_cup;
#ElementList(required = false, entry = "Rates")
public List<Rates> rates;
}
public static class Rates {
#Attribute(name = "rate", required = false)
public String rate;
}
}
You are on the right way. As you have many Rates here, better name the class more with more context and set the xml name via Annotation.
I've separated the XMLResponse and Rates classes, but this doesn't make any difference.
The class XMLRespone maps the <xml>...</xml> part:
#Root(name = "xml")
public class XMLRespone
{
#Element(name = "Rates")
private Rates rates;
// ...
}
All types for <Rates...>...</Rates> is done by Rates class (and it's inner classes, which map the structure:
#Root(name = "Rates")
public class Rates
{
#Element(name = "Rates")
private RateList rates;
// ...
public static class RateList
{
#ElementList(entry = "Rates", inline = true)
private ArrayList<RateValue> values;
#Attribute(name = "winrate_cap", required = true)
private int winrateCap;
// ...
}
public static class RateValue
{
#Attribute(name = "rate", required = true)
private int rate;
// ...
}
}
Deserializing your XML gives this output (using generated toString()):
XMLRespone{rates=Rates{rates=RateList{values=[RateValue{rate=323}, RateValue{rate=343}, RateValue{rate=2338}, RateValue{rate=233}], winrateCap=9999}}}

Correct way to parse XML using Simple Framework

I'm using Simple framework to deserialize an xml on my android app .
The issue is on a portion of the xml because I can get the objects of the other portions of it .
Here is the part of the xml that I struggle with :
<webtv name="channelname" date="2014-10-31">
<config>
<pathVideo extension="mp4">http://vodflash.channelname.com/tv_conn/1/country/</pathVideo>
<pathBigImage extension="jpg">http://vodflash.channelname.com/tv_conn/1/country/pic/320x180/</pathBigImage>
<pathImage extension="jpg">http://vodflash.channelname.com/tv_conn/1/country/pic/160x90/</pathImage>
<pays>GB;IE</pays>
</config>
Here is my XmlMapper class :
#Root(name="webtv", strict = false)
public class XmlModelMapper {
public ConfigObject getConfigObjects() {
return configObject;
}
#Element(name="config")
public ConfigObject configObject = new ConfigObject();
#ElementList(name = "itemList")
private List<Videos> itemList = new ArrayList<Videos>();
public List<Videos> getItemList() {
return itemList;
}
#ElementList(name = "chaineList")
private List<Chaine> chaineList = new ArrayList<Chaine>();
public List<Chaine> getChaineList() {
return chaineList;
}
}
If I change my mapper class to this :
#Root(name="webtv", strict = false)
public class XmlModelMapper {
public List<ConfigObject> getConfigObjects() {
return configObject;
}
//change is here using a list not an object
#ElementList(name="config")
public List<ConfigObject> configObjects = new ArrayList<ConfigObject>();
#ElementList(name = "itemList")
private List<Videos> itemList = new ArrayList<Videos>();
public List<Videos> getItemList() {
return itemList;
}
#ElementList(name = "chaineList")
private List<Chaine> chaineList = new ArrayList<Chaine>();
public List<Chaine> getChaineList() {
return chaineList;
}
}
and the log the size of the List I get 4 which is correct , but how to get each object in a distinct way including the extension (attribute)
Please help me solving this issue .
Thanks
#ElementList(name="config")
public List<ConfigObject> configObjects = new ArrayList<ConfigObject>();
This wont match with your xml listed above. While the #Element-based solution (example 1) will create a proper config-tag, the list adds another "list-tag"; here's an example:
<config> <!-- This is the list's tag, wrapping all elements -->
<config> <!-- This is the actual element's tag -->
<pathVideo extension="mp4">http://video.com</pathVideo>
<pathBigImage extension="jpg">http://bigimg.com</pathBigImage>
<pathImage extension="jpg">http://image.com</pathImage>
<pays>GB;IE</pays>
</config>
<!-- some more <config>...</config> -->
</config>
The solution: Use athe inline list - those don't have the "list-tag".
#ElementList(name = "config", inline = true, entry = "config")
public List<ConfigObject> configObjects = new ArrayList<>();
For the sake of completeness, here are my classes used for testing:
class Chaine { /* empty - enough for testing */ }
class Videos { /* empty - enough for testing */ }
#Root
public class ConfigObject
{
#Element(name = "pathVideo")
private PathConfig video;
#Element(name = "pathBigImage")
private PathConfig bigImage;
#Element(name = "pathImage")
private PathConfig image;
#Element(name = "pays")
private String pays;
// ...
}
#Root
public class PathConfig
{
#Attribute(name = "extension")
private String extension;
#Text
private String path;
// ...
}
Solved my issue and learned alot about simple framework which is great and light weight , but I prefer ollo's answer
Please discard ormlite annotations
My classes :
#Root(name="webtv", strict = false)
public class XmlModelMapper {
public ConfigObject getConfigObject() {
return configObject;
}
#Element(name="config")
public ConfigObject configObject = new ConfigObject();
//rest of the elements
...
}
and
#Root(name = "config" , strict = false)
public class ConfigObject {
#Element
PathVideo pathVideo;
#Element
PathImage pathImage;
#Element
PathBigImage pathBigImage ;
//rest of the elements
}
and
#Root(name = "pathImage")
#DatabaseTable(tableName = "imageconfig")
public class PathImage {
#DatabaseField(generatedId = true)
Integer id ;
#Attribute
#DatabaseField
String extension;
#Text
#DatabaseField
String pathImage;
//rest of the elements...
}
and
#Root(name = "pathVideo")
#DatabaseTable(tableName = "videoconfig")
public class PathVideo {
#DatabaseField(generatedId = true)
Integer id ;
#Attribute
#DatabaseField
String extension;
#Text
#DatabaseField
String pathVideo;
}
and finally
#Root(name = "pathBigImage")
#DatabaseTable(tableName = "bigimageconfig")
public class PathBigImage {
#DatabaseField(generatedId = true)
Integer id ;
#Attribute
#DatabaseField
String extension;
#Text
#DatabaseField
String pathBigImage;
//rest of the elements...
}

Categories