Unable to map String to Object [duplicate] - java

This question already has answers here:
Case insensitive JSON to POJO mapping without changing the POJO
(8 answers)
Closed 3 years ago.
I have a JSON value as follows in String format.
{
"Sample": {
"name": "some name",
"key": "some key"
},
"Offering": {
"offer": "some offer",
"amount": 100
}
}
Now if I try to map this as follows, it works and maps fine.
//mapper is ObjectMapper;
//data is the above json in String format
Map vo = mapper.readValue(data, Map.class);
But I want to map it to a custom Data class as follows.
Data vo = mapper.readValue(data, Data.class);
When I do this, the result of vo is null.
Refer to following on how the Data class is structured.
#Getter
#Setter
public class Data {
private Sample sample;
private Offering offering;
}
#Getter
#Setter
public class Offering {
public String offer;
public int amount;
}
#Getter
#Setter
public class Sample {
private String name;
private String key;
}
Please advice what I am doing wrong. Thanks.

There seems to be issue with Word Case here.
Its "Sample" in your json.
But its "sample" in java file.
Similarly for Offering.
You can of-course use #JsonProperty if you want to map without changing the case.

There are two options :
if you can change your json - you have to change Sample to sample and Offering to offering
Change your Data class to :
#Getter
#Setter
public class Data {
#JsonProperty("Sample")
private Sample sample;
#JsonProperty("Offering")
private Offering offering;
}
In the second option you have to tell Jackson what properties of your input json you want to map to which properties of your class, because by default it will try to map to lowercase properties names.

It may be due to different mapping names of fields in json string and Demo model. "Sample" in json string but "sample" in model class.
You can use #JsonProperty
#JsonProperty("Sample")
private Sample sample;
#JsonProperty("Offering")
private Offering offering;

Related

Ignore property when converting object to json string but not when converting string to Object using by Jackson library [duplicate]

This question already has answers here:
Only using #JsonIgnore during serialization, but not deserialization
(10 answers)
Jackson: how to prevent field serialization [duplicate]
(9 answers)
Closed 3 months ago.
I have a REST API that provides me a JSON. In the json I receive from the REST API, there is a ID property I want to read so I can do some checking on it. However, when I want to write back to the web server, the ID property must not be present in the response JSON string. So it has to be a write-only property, but simply changing a property to write-only prevents be to check what the value is of that property.
For example, I create a new product:
public class Product {
//This feild should not be ignore when I convert JSON to object.
// But the same one should be ignore when I convert the object to Json
#JsonProperty
public String id;
#JsonProperty
public String name;
}
GET response:
{
"id": 1,
"name": "Product1"
}
POST Wrong:
{
"id": 1, <-- This should be in the response
"name": "Product1"
}
POST should be:
{
"name": "Product1"
}
To prevent a certain field from being serialized, you can use access attribute of the #JsonProperty annotation by assigning it to JsonProperty.Access.WRITE_ONLY.
public static class Product {
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
public String id;
public String name;
// getters, setters
}
Usage example:
String incomingJson = """
{
"id": 1,
"name": "Product1"
}
""";
Product product = mapper.readValue(incomingJson, Product.class);
String result = mapper.writeValueAsString(product);
System.out.println(result);
Output:
{"name":"Product1"}

How do i ignore a specific field from an list in JSON response

I am trying to ignore a specified field from a list during deserialization. I am not sure how do i do that for a field that sits inside a list. Below is my json and response class
Sample json
{
"key": {
"rowKey": "123"
},
"names": [
{
"firstName": "JON ",
"firstNameFormatted": "JON"
}
]
}
Response class
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#ToString
public class Data {
private Map<String,Object> key;
private List<Map<String,Object>> names;
}
Here i would like to ignore
firstNameFormatted
from my json response but i am not sure how to do that using jackson for a field that is inside a list ?
Jackson has a solution for that. Simply use #JsonIgnore
You can see it in the example below
#JsonIgnore
public String getPassword() {
return password;
}
Now the password information won’t be serialized to JSON.
Also you can try with #JsonIgnoreProperties

Converting the unstructured object in java

I'm using MongoDb for unstructured documents. When I do the aggregations, I'm getting final output as unstructured objects. I post some sample data for the easiness. Actual objects have many fields.
Eg :
[
{ _id : "1", type: "VIDEO", videoUrl : "youtube.com/java"},
{ _id : "2", type: "DOCUMENT", documentUrl : "someurl.com/spring-boot-pdf"},
{ _id : "3", type: "ASSESSMENT", marks : 78}
]
The respective class for the types of above objects are
#Data
public class Video{
private String _id;
private String type;
private String videoUrl;
}
#Data
public class Document{
private String _id;
private String type;
private String documentUrl;
}
#Data
public class Assessment{
private String _id;
private String type;
private Integer marks;
}
Since I can't specify the converter class, I get all objects as list of Object.class which is a general type for all.
List<Object> list = mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(YOUR_COLLECTION.class), Object.class).getMappedResults();
It's working, but this is not readable and not maintainable for backend and front-end developers (eg : swagger ui). So I came up with a solution, that put all fields as a class.
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
class MyConvetor{
private String _id;
private String type;
private String videoUrl;
private String documentUrl;
private Integer marks;
}
Here Jackson helps to ignore all null fields
Now I can use MyConverter as Type
List<MyConverter> list = mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(YOUR_COLLECTION.class), MyConverter.class).getMappedResults();
But I feel this is not a good practice when we implementing a standard application. I'd like to know, is there any way to avoid the general type class (e.g. extending any abstract class)? Or is this the only way I can do?
I don't think so (or I don't know) if MongoDB in Java provides this kind of dynamic conversion by some field (it would require specify what field and what classes). But you can do it by hand.
First, you need to define your types (enum values or some map) for matching string to class. You can create abstract parent class (eg. TypedObject) for easier usage and binding all target classes (Video, Document, Assessment) .
Next you have to read and map values from Mongo to anything because you want to read all data in code. Object is good but I recommend Map<String, Object> (your Object actually is that Map - you can check it by invoking list.get(0).toString()). You can also map to String or DBObject or some JSON object - you have to read "type" field by hand and get all data from object.
At the end you can convert "bag of data" (Map<String, Object> in my example) to target class.
Now you can use converted objects by target classes. For proving these are actually target classes I print objects with toString all fields.
Example implementation
Classes:
#Data
public abstract class TypedObject {
private String _id;
private String type;
}
#Data
#ToString(callSuper = true)
public class Video extends TypedObject {
private String videoUrl;
}
#Data
#ToString(callSuper = true)
public class Document extends TypedObject {
private String documentUrl;
}
#Data
#ToString(callSuper = true)
public class Assessment extends TypedObject {
private Integer marks;
}
Enum for mapping string types to classes:
#RequiredArgsConstructor
public enum Type {
VIDEO("VIDEO", Video.class),
DOCUMENT("DOCUMENT", Document.class),
ASSESSMENT("ASSESSMENT", Assessment.class);
private final String typeName;
private final Class<? extends TypedObject> clazz;
public static Class<? extends TypedObject> getClazz(String typeName) {
return Arrays.stream(values())
.filter(type -> type.typeName.equals(typeName))
.findFirst()
.map(type -> type.clazz)
.orElseThrow(IllegalArgumentException::new);
}
}
Method for converting "bag of data" from JSON to your target class:
private static TypedObject toClazz(Map<String, Object> objectMap, ObjectMapper objectMapper) {
Class<? extends TypedObject> type = Type.getClazz(objectMap.get("type").toString());
return objectMapper.convertValue(objectMap, type);
}
Read JSON to "bags of data" and use of the above:
String json = "[\n" +
" { _id : \"1\", type: \"VIDEO\", videoUrl : \"youtube.com/java\"},\n" +
" { _id : \"2\", type: \"DOCUMENT\", documentUrl : \"someurl.com/spring-boot-pdf\"},\n" +
" { _id : \"3\", type: \"ASSESSMENT\", marks : 78}\n" +
"]";
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
List<Map<String, Object>> readObjects = objectMapper.readValue(json, new TypeReference<>() {});
for (Map<String, Object> readObject : readObjects) {
TypedObject convertedObject = toClazz(readObject, objectMapper);
System.out.println(convertedObject);
}
Remarks:
In example I use Jackson ObjectMapper for reading JSON. This makes the example and testing simpler. I think you can replace it with mongoTemplate.aggregate(). But anyway I need ObjectMapper in toClazz method for converting "bags of data".
I use Map<String, Object> instead of just Object. It is more complicated: List<Map<String, Object>> readObjects = objectMapper.readValue(json, new TypeReference<>() {});. If you want, you can do something like this: List<Object> readObjects2 = (List<Object>) objectMapper.readValue(json, new TypeReference<List<Object>>() {});
Result:
Video(super=TypedObject(_id=1, type=VIDEO), videoUrl=youtube.com/java)
Document(super=TypedObject(_id=2, type=DOCUMENT), documentUrl=someurl.com/spring-boot-pdf)
Assessment(super=TypedObject(_id=3, type=ASSESSMENT), marks=78)
Of course you can cast TypedObject to target class you need (I recommend checking instance of before casting) and use:
Video video = (Video) toClazz(readObjects.get(0), objectMapper);
System.out.println(video.getVideoUrl());
I assumed you read whole collection once and you get all types mixed up in one list (as in example in your question). But you can try find documents in MongoDB by field "type" and get data separately for each of type. With this you can easily convert to each type separately.

how to search through json object that contains mutliple json objects and update particular value of sub object in java?

I have json as follows
{
"students":[{
"name":"abc",
"id":"1",
"age":"34",
"hobby":"sports"
},{
"name":"pqr",
"id":"2",
"age":"25",
"hobby":"dance"
},
]
}
and I want to update the value of {
"name":"pqr",
"id":"2",
"age":"25",
"hobby":"dance"
}, this hobby to "reading"
can someone help me? I'm new to json objects
Convert your json string to a java object and update the value using setter method
Go through this tutorial https://www.mkyong.com/java/jackson-2-convert-java-object-to-from-json/.
first step you have to make a student class
public class Student{
private String name;
private String id;
private String age;
private String hobby;
}
second step use GSON lib with arrayList of Student like this
ArrayList<Student>arrayList=new Gson().fromJson(MyJsonStructure,Student.class);

How to correctly use Jackson, Dropwizard and JSON patterns

I'm trying to workout how I should be structuring my JSON objects to best comply with http://jsonapi.org/format/
If I used the following classes:
Page (main object)
public class Page {
#Id
#ObjectId
private String id; //Used for mongodb
#JsonProperty
private String type;
#JsonProperty
private Attribute attributes;
#JsonProperty
private Meta meta;
public Page() {
// Jackson deserialization
}
// Getters and setters
}
Attributes (nested into page)
public class Attribute {
#JsonProperty
private Date created = new Date();
#JsonProperty
private String title;
#JsonProperty
private String body;
public Attribute() {
// Jackson deserialization
}
public Attribute(Date created, String title, String body) {
this.created = created;
this.title = title;
this.body = body;
}
// Getters and setters
}
Meta (nested into page)
public class Meta {
#JsonProperty
private List<String> authors;
public Meta() {
}
public Meta(List<String> authors) {
this.authors = authors;
}
// Getters and setters
}
I can create this object with a post such as:
{
"type": "page",
"attributes": {
"title": "This is the title",
"body": "<p>This is a long section of html, other stuff</p>"
},
"meta": {
"authors": [
"Steve",
"John",
"Sam"
]
}
}
And the resulting JSON object is created:
{
id:"56cbed5036f66b05dc2df841",
type:"page",
attributes:{
created:1456205138886,
title:"This is the title",
body:"<p>This is a long section of html, other stuff</p>"
},
meta:{
authors:[
"Steve",
"John",
"Sam"
]
}
}
The question(s):
Is creating multiple classes the way I have the optimal/correct way of creating nested JSON objects, and should I be trying to wrap this all inside "data:" as per the link above states is a MUST do? If that is the case should I create a single POJO called Data which contains the Page object?
When looking for information around this type of thing all I seem to be able to find is people asking how to deserialize JSON into POJOs, which isn't what I'm looking for.
Really want to find some best practises here for writing APIs.
you should start then from what kind of 'behavior' your objects expose and how you want your API to expose.
Alhough there is no silver bullet, there is a good amount of literature around which can guide you in the right direction of how to model your API (and in turn your objects)
here are a few links:
http://restcookbook.com
http://www.infoq.com/minibooks/domain-driven-design-quickly
Value vs Entity objects (Domain Driven Design)
Personally, and -in general-, I create POJOs, but like also #cricket_007 mentioned it's kind of opinionated.
HTH.

Categories