I am using Jackson library to try and parse my JSON file. My JSON is actually an ARRAY of JSON Objects:
JSON ARRAY:
[
{
"Id" : "0",
"name" : "John"
},
{
"Id" : "1",
"name" : "Doe"
}
]
POJO CLASS:
#JsonIgnoreProperties(ignoreUnknown = true)
public class QuestData {
private String Id;
private String name;
public String getId() {
return Id;
}
public String getName() {
return name;
}
}
PARSING JSON:
private void parseJSON(File jsonFile) {
try {
byte[] jsonData = Files.readAllBytes(jsonFile.toPath());
System.out.println(new String(jsonData));
ObjectMapper mapper = new ObjectMapper();
List<QuestData> questDataList = mapper.readValue(jsonData, mapper.getTypeFactory().constructCollectionType(List.class, QuestData.class));
System.out.println("Read values: " + questDataList.get(0).getId());
} catch (IOException e) {
e.printStackTrace();
}
}
My first print statement prints correct Json data back (as String).
But next print statement says NULL. I even tried to itreate over entire list ot see if there is something that is not null, but with no luck.
I do not know what I am doing wrong here.
Jackson will by default use setter methods to set fields. So add setters like:
#JsonProperty("Id") // otherwise Jackson expects id for setId
public void setId(String id) {
Id = id;
}
Alternatively, tell Jackson to look for fields with this config:
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
In this case Jackson will match the name of the field in the class Id with the one in JSON Id
Just add the #JsonProperty annotation to the Id property in your QuestData class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class QuestData {
#JsonProperty("Id")
private String Id;
private String name;
public String getId() {
return Id;
}
public String getName() {
return name;
}
}
Related
I am struggling to deserialise complex nested Json data into Java objects I think my class structure is wrong. Here is my Json data:
{
"resultsPerPage": 20,
"startIndex": 0,
"totalResults": 2,
"result": {
"dataType": "CPE",
"feedVersion": "1.0",
"cpeCount": 2,
"feedTimestamp": "2021-03-19T13:06",
"cpes": [
{
"deprecated": false,
"cpe23Uri": "cpe:2.3:o:microsoft:windows_10:1511:*:*:*:*:*:x64:*",
"lastModifiedDate": "2015-12-09T17:28Z",
"titles": [
{
"title": "Microsoft Windows 10 1511 64-bit",
"lang": "en_US"
}
],
"refs": [
{
"ref": "https://www.microsoft.com/en-us/",
"type": "Vendor"
}
],
"deprecatedBy": [],
"vulnerabilities": [
"CVE-2016-0174",
"CVE-2016-0171"
]
}
Here is the class I map the Json data to:
public class RESPONSE {
Result result;
}
class Result {
List<Cpes> cpes;
}
class Cpes {
String cpe23Uri;
List<Titles> titles;
List<String> vulnerabilities;
}
class Titles{
String title;
}
When I debug my code r in the below code is null and I think it's because my RESPONSE class isn't set up right.
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
RESPONSE r = mapper.readValue(response.getContent(), RESPONSE.class);
System.out.println(r);
Your object model should match the structure of the JSON you are trying to read. For example, it'll have to look something like the following:
public class Response {
private int resultsPerPage;
private int startIndex;
private int totalResults;
private Result result;
// Should include getters and setters
}
public class Result {
private String dataType;
private String feedVersion;
private int cpeCount;
private String feedTimestamp;
private CPE[] cpes;
// Should include getters and setters
}
public class CPE {
private boolean deprecated;
private String cpe23Uri;
private String lastModifiedDate;
private Title[] titles;
private Ref[] refs;
private String[] deprecatedBy;
private String[] vulnerabilities;
// Should include getters and setters
}
public class Title {
private String title;
private String lang;
// Should include getters and setters
}
public class Ref {
private String ref;
private String type;
// Should include getters and setters
}
Note that to keep the code sample short, I've omitted the getters and setters.
Edit: As Tugrul pointed out below, since fail on unknown property is disabled, it won't fail if there are missing fields in your model. The only issue is the missing getters and setters.
I also found another way to solve this issue for future reference.
I used a tree data structure to access my Json fields which means I can just declare a flat class:
public class Test {
private String cpe23Uri;
private String title;
private List<String> vulnerabilities;
public String getCpe23Uri() {
return cpe23Uri;
}
public void setCpe23Uri(String cpe23Uri) {
this.cpe23Uri = cpe23Uri;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<String> getVulnerabilities() {
return vulnerabilities;
}
public void setVulnerabilities(List<String> vulnerabilities) {
this.vulnerabilities = vulnerabilities;
}
}
I then mapped using a Tree
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JsonNode resultNode = mapper.readTree(response.getContent());
Test t = new Test();
t.setCpe23Uri(resultNode.get("result").get("cpes").get(0).get("cpe23Uri").textValue());
I have the following Json
{
"coreId" : "1",
"name" : "name",
"additionalValueList" : [
{
"columnName" : "allow_duplicate",
"rowId" : "10",
"value" : "1"
},
{
"columnName" : "include_in_display",
"rowId" : "11",
"value" : "0"
},
...e.t.c
]
},
...e.t.c
and Java class
class DTO {
#JsonProperty("coreId")
private Integer id;
private String name;
private Boolean allowDuplicate;
private Boolean includeInDisplay;
}
How I can easily map values from 'additionalValueList' to corresponding java fields.For example Json value from field 'columnName' - 'allow_duplicate' = DTO.allowDuplicate.
Actually I know how to do it with custom deserializers with #JsonDeserialize annotation and smth like this.Bu I have 40+ DTO and it is not a good idea to create own deserializer for each filed. I am looking for solution to have for example 1 deserializer(since values structure in 'additionalValueList' are the same for all entities) and to pass parameter(field name that I want to map to that field) to custom deserializer that will find in 'additionalValueList' entity with 'column Name' = parameter(that I passed from annotation) and return 'value'.
Example
class DTO {
#JsonProperty("coreId")
private Integer id;
private String name;
#JsonDeserialize(using = MyCustDeser.class,param = allow_duplicate)
private Boolean allowDuplicate;
#JsonDeserialize(using = MyCustDeser.class,param = include_in_display)
private Boolean includeInDisplay;
}
It will be a good solution but maybe not easy to achieve.However I will be very grateful for all your advices.Thank you.
Create a Converter class, then specify it on the DTO class.
The following code uses public fields for the simplicity of the example.
/**
* Intermediate object used for deserializing FooDto from JSON.
*/
public final class FooJson {
/**
* Converter used when deserializing FooDto from JSON.
*/
public static final class ToDtoConverter extends StdConverter<FooJson, FooDto> {
#Override
public FooDto convert(FooJson json) {
FooDto dto = new FooDto();
dto.name = json.name;
dto.id = json.coreId;
dto.allowDuplicate = lookupBoolean(json, "allow_duplicate");
dto.includeInDisplay = lookupBoolean(json, "include_in_display");
return dto;
}
private static Boolean lookupBoolean(FooJson json, String columnName) {
String value = lookup(json, columnName);
return (value == null ? null : (Boolean) ! value.equals("0"));
}
private static String lookup(FooJson json, String columnName) {
if (json.additionalValueList != null)
for (FooJson.Additional additional : json.additionalValueList)
if (columnName.equals(additional.columnName))
return additional.value;
return null;
}
}
public static final class Additional {
public String columnName;
public String rowId;
public String value;
}
public Integer coreId;
public String name;
public List<Additional> additionalValueList;
}
You now simply annotate the DTO to use it:
#JsonDeserialize(converter = FooJson.ToDtoConverter.class)
public final class FooDto {
public Integer id;
public String name;
public Boolean allowDuplicate;
public Boolean includeInDisplay;
#Override
public String toString() {
return "FooDto[id=" + this.id +
", name=" + this.name +
", allowDuplicate=" + this.allowDuplicate +
", includeInDisplay=" + this.includeInDisplay + "]";
}
}
Test
ObjectMapper mapper = new ObjectMapper();
FooDto foo = mapper.readValue(new File("test.json"), FooDto.class);
System.out.println(foo);
Output
FooDto[id=1, name=name, allowDuplicate=true, includeInDisplay=false]
I am facing an issue while converting an JSON to a Java Object.
My Json is as below
{
"_id":{
"$oid":"5981428cf1aa82a313540b76"
},
"productId":1,
"name":"The Big Lebowski",
"currency":{
"currency":"USD",
"value":40.5
}
}
I am retrieving json as DBObject for Product from the MongoDB database.
DBObject dbObject = productsCollection.findOne(searchQuery);
if(dbObject != null)
{
Product product = (Product) AppUtils.fromDBObject(dbObject, Product.class);
return Optional.of(product);
}
Product is return as
Product[productId = 1, productName= null, currencyPrice = null]
My fromDBObject method in AppUtils.java is as below :
public static Object fromDBObject(DBObject dbObj, Class<?> clazz)
{
String json = dbObj.toString();
return new Gson().fromJson(json, clazz);
}
My POJO is as below :
public class Product
{
private long productId;
private String productName;
private CurrencyPrice currencyPrice;
// getter and setter
}
public class CurrencyPrice
{
private double value;
private String currency;
// getter and setter
}
I am unable to understand where it is going wroing for the DBObject object with json to translate to Product object.
Thanks !
try changing your POJO property names to match
public class Product
{
private long productId;
private String name;
private CurrencyPrice currency;
// getter and setter
}
I'm writing a REST API in Java and Play Framework, however I ran into a problem with Jackson serialization. I have the following model:
#Entity
#JsonRootName("country")
public class Country extends BaseModel<Country> {
private String name;
private Collection<City> cities;
...
}
The Jackson object mapper configuration:
ObjectMapper mapper = Json.newDefaultMapper()
.configure(SerializationFeature.WRAP_ROOT_VALUE, true)
.configure(SerializationFeature.INDENT_OUTPUT, true);
When I serialize a Country model however,
Country c = service.get(id);
return ok(toJson(c));
I get the following output:
{
"ObjectNode" : {
"country" : {
"id" : 5,
"name" : "Holandija",
"cities" : [ ]
}
}
}
The expected output would be:
{
"country" : {
"id" : 5,
"name" : "Holandija",
"cities" : [ ]
}
}
Why is Jackson adding the extra ObjectNode node? How to get rid of it?
It seems you have a problem in toJson method. The following code works perfect (the original class Country was modified for simplicity):
#Entity
#JsonRootName(value = "country")
public class Country {
public int id;
public String name;
public Collection<String> cities;
public Country() {
}
public Country(int id, String name) {
this.id = id;
this.name = name;
}
}
Test:
#Test
public void testRootJsonMapping() throws JsonProcessingException {
Country tested = new Country(55, "Neverland");
ObjectMapper mapper = new ObjectMapper()
.configure(SerializationFeature.WRAP_ROOT_VALUE, true)
.configure(SerializationFeature.INDENT_OUTPUT, true);
String json = mapper.writeValueAsString(tested);
System.out.println("json:" + json);
}
Test output:
json:{
"country" : {
"id" : 55,
"name" : "Neverland",
"cities" : null
}
}
If json conversion is done with Play API Json, it should be configured on startup with appropriate mapping options:
private void configureJson() {
ObjectMapper mapper = new ObjectMapper()
.configure(SerializationFeature.WRAP_ROOT_VALUE, true)
.configure(SerializationFeature.INDENT_OUTPUT, true);
Json.setObjectMapper(mapper);
}
Here you can read more details of how to customize Json conversion in Play.
I want to deserialize the following JSON object:
{
"id":"001",
"module_name":"Users",
"name_value_list":
{
"user_name": {"name":"user_name", "value":"admin"},
"full_name": {"name":"full_name", "value":"LluĂs Pi"},
"city": {"name":"full_name", "value":"Barcelona"},
"postal_code": {"name":"postal_code", "value":"08017"},
...
}
}
into some Java object like this:
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)
public class UserEntry
{
private String id;
private String moduleName;
private Person nameValueList;
public String getId()
{
return id;
}
public String getModuleName()
{
return moduleName;
}
public Person getPerson()
{
return nameValueList;
}
}
where Person is the following class:
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)
class Person
{
private String userName;
private String fullName;
private String city;
private String postalCode;
}
using Jackson but I get a deserialization error.
If I change the type of field nameValueList to a Map all the deserialization process goes with no problem and I get a map where the key is the "name" value and the value is the "value" value.
So my question is: is there any simple, or no so simple, way to deserialize this kind of JSON object to a Java Pojo with properties prop_1, prop_2, prop_3and prop_4?
{
"name_value_list":
{
"prop_1": {"name":"prop_1", "value":"value_1"},
"prop_2": {"name":"prop_2", "value":"value_2"},
"prop_3": {"name":"prop_3", "value":"value_3"},
"prop_4": {"name":"prop_4", "value":"value_4"},
...
}
}
Not very simple and not very clean. However you can do it by implementing a any setter field for the JSON attributes in the Person class which don't match any attribute on your UserEntry POJO.
#JsonAnySetter
public void putUserField(String userKey, Map<String, String> userValue)
throws NoSuchFieldException {
String actualFieldName = getActualFieldName(userKey);
Field field = this.getClass().getDeclaredField(actualFieldName);
field.setAccessible(true);
ReflectionUtils.setField(field, this, userValue.get("value"));
}
private String getActualFieldName(String userKey) {
return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, userKey);
}
In addition to that, I had to change the Jackson attributes for the Person class to
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY,
getterVisibility = JsonAutoDetect.Visibility.NONE)
for it to work for attributes like "city" which don't need any name transformation because jackson tries to directly set the field which fails.