This question already has an answer here:
Jackson JSON Deserialization with Root Element
(1 answer)
Closed 2 years ago.
I am trying to parse a JSON using Jackson,here is my class
#JsonIgnoreProperties(ignoreUnknown = true)
public class Customer {
private String name;
public void setName(String n) {
name = n;
}
public String getName() {
return name;
}
}
and the runner class
public class jsontoObj {
public static void main(String args[]) throws IOException {
ObjectMapper mapper = new ObjectMapper();
String json = "{\n" +
" \"customer\":\n" +
" {\n" +
" \"name\": \"John Doeyy\"\n" +
" }\n" +
"}";
Customer customer = mapper.readValue( json, Customer.class);
System.out.println(customer.getName());
}
}
the setName method is working but getting null in customer.getName().
I don't want to use moxy
A correct json according to your mapping would be
String json = "{ \"name\": \"John Doeyy\" }";
I removed the embeded "customer" object
Related
The response of a WS is a json with first capitalize letters. I'm trying to encapsulate the response in a new MyResponse obj having lowercase first letters.
I'm using Jackson.
At first I have my models:
public class Telephone {
private String country;
private String prefix;
//getters and setters
}
public class Position {
private String x;
private String y;
//getters and setters
}
public class Root {
#JsonProperty("Telephone")
private List<Telephone> telephone;
#JsonProperty("Position")
private List<Position> position;
//getters and setters
}
public class MyResponse {
private final Root root;
private final String now;
public MyResponse(Root root, String now) {
this.root = root;
this.now = now;
}
//getters
}
As you can see above, I used #JsonProperty in my Root class because I want to map my response using a first lowercase letter.
Now I have my RestController:
#Controller
public class RestController {
#GetMapping("/my-controller")
ResponseEntity<String> myController() {
//Simulating the request to my ws to get my json string
String jsonString = "{\n" +
" \"Telephone\":[\n" +
" {\n" +
" \"country\":\"ES\",\n" +
" \"prefix\":\"+34\"\n" +
" },\n" +
" {\n" +
" \"country\":\"FR\",\n" +
" \"prefix\":\"+33\"\n" +
" },\n" +
" {\n" +
" \"country\":\"EN\",\n" +
" \"prefix\":\"+44\"\n" +
" }\n" +
" ],\n" +
" \"Position\":[\n" +
" {\n" +
" \"x\":\"123.23\",\n" +
" \"y\":\"98.93\"\n" +
" },\n" +
" {\n" +
" \"x\":\"250.99\",\n" +
" \"y\":\"43.89\"\n" +
" }\n" +
" ]\n" +
"}";
ObjectMapper om = new ObjectMapper();
Root root = null;
try {
root = om.readValue(jsonString, Root.class);
MyResponse myResponse = new MyResponse(root, LocalDateTime.now().toString());
String responseAsString = om.writeValueAsString(myResponse);
return new ResponseEntity<>(responseAsString, HttpStatus.OK);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
As you can see in the snippet of code above, at the beginning I got the json string (in my real code calling the WS) and I deserialized it into a Java POJO using the readValue method:
root = om.readValue(jsonString, Root.class);
Then I created my MyResponse obj using the deserialized POJO:
MyResponse myResponse = new MyResponse(root, LocalDateTime.now().toString());
And at the end, I serialized myResponse obj to String using om.writeValueAsString and I returned it to my frontend:
String responseAsString = om.writeValueAsString(myResponse);
return new ResponseEntity<>(responseAsString, HttpStatus.OK);
Since MyResponse obj is serialized and deserialized both using my Root #JsonProperty (s)
I get:
{
"root":{
"Telephone":[
{
"country":"ES",
"prefix":"+34"
},
{
"country":"FR",
"prefix":"+33"
},
{
"country":"EN",
"prefix":"+44"
}
],
"Position":[
{
"x":"123.23",
"y":"98.93"
},
{
"x":"250.99",
"y":"43.89"
}
]
},
"now":"2021-06-24T11:18:04.077612"
}
That is not what I am trying to do: I have capitalize letters in my response.
How can I solve this problem? Should I use two different classes for serialization and deserialization?
You can specify your Jackson annotations on getters and setters as well to let them behave differently. Like this:
public class Root {
private List<Telephone> telephone;
#JsonProperty("Telephone")
private void setTelephone(String telephone) {
this.telephone = telephone;
}
#JsonProperty("telephone")
private String getTelephone() {
this.telephone = telephone;
}
}
I've tried what suggested by Felix. But it didn't work, I got the following error:
Conflicting/ambiguous property name definitions found multiple explicit names: but also implicit accessor
After a while I was able to solve my problem in this way:
public class Root {
private List<Telephone> telephone;
private List<Position> position;
#JsonCreator
public Root(#JsonProperty("Telephone") List<Telephone> telephon, #JsonProperty("Position") List<Position> position) {
this.telephon = telephon;
this.position = position;
}
//getters and setters
}
The annotation #JsonCreator is used in deserialization phase only.
When one deserializes Jackson uses the constructor annotated with #JsonCreator. In serialization phase Jackson uses the fields name to convert the obj into String.
I have a JSON array string like below.
"[
{
"id" : "123",
"name":"John Doe",
"Address":"53/A"
},
{
"id" : "1234",
"name":"John Doe1",
"Address":"53/AB"
}
]"
I have a POJO class which maps with the inner JSON objects like below.
class User {
String id;
String name;
String Address;
//Getters & Setters
}
I want to create POJO objects from the above String and create a User ArrayList. How can I achieve this ? (using a library like Gson is fine). Thank you.
Gson, since you mentioned it, offers a method (link) which is exactly what you're looking for.
Gson.fromJson(java.lang.String json, java.lang.Class<T> classOfT)
If you want to deserialize a generic type (e.g. a list), docs advise you to use this method:
Gson.fromJson(java.lang.String json, java.lang.reflect.Type typeOfT)
There is also a tutorial (link) which tackles a few use-cases close to what you want to do.
ArrayList<LinkedTreeMap> list = gson.fromJson(json, ArrayList.class);
List<User> users= list.stream()
.map(s -> gson.fromJson(gson.toJson(s), User.class))
.collect(Collectors.toList());
You can use Jackson ObjectMapper and its very simple. below is the example code.
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.List;
public class JsanParserExample {
public static void main(String[] args) throws IOException {
String json = "[\n" +
" {\n" +
" \"id\" : \"123\",\n" +
" \"name\":\"John Doe\",\n" +
" \"address\":\"53/A\"\n" +
" \n" +
" },\n" +
" {\n" +
" \"id\" : \"1234\",\n" +
" \"name\":\"John Doe1\",\n" +
" \"address\":\"53/AB\"\n" +
" \n" +
" }\n" +
" \n" +
"]";
ObjectMapper objectMapper = new ObjectMapper();
List<User> userList = objectMapper.readValue(json,new TypeReference<List<User>>() {});
userList.forEach(user -> System.out.println(user.getId()));
}
private static class User {
private String id;
private String name;
private String address;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
}
You can use Jackson. Simple example you can find here: https://www.baeldung.com/jackson-collection-array#to-array
Example:
ObjectMapper mapper = new ObjectMapper();
String jsonArray = "[{\"stringValue\":\"a\",\"intValue\":1,\"booleanValue\":true}, {\"stringValue\":\"bc\",\"intValue\":3,\"booleanValue\":false}]";
MyDto[] asArray = mapper.readValue(jsonArray, MyDto[].class);
I'm having a hard time processing the below JSON with Java, which is being returned from on an external Ansible playbook:
{"Sample":
{
"tag_description":"abc","tag_category_id":"def","tag_id":"ghi"
},
"Sample1":
{
"tag_description":"jkl","tag_category_id":"mno","tag_id":"pqr"
}
}
I've been able to successfully parse one section of the JSON using a custom deserializer, though it only ever gets the first section. Any ideas are hugely appreciated.
#JsonComponent
public class TagSerializer extends JsonDeserializer<Tag> {
#Override
public Tag deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException,
JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
JsonFactory factory = mapper.getFactory();
JsonNode treeNode = jsonParser.getCodec().readTree(jsonParser);
Iterator<Map.Entry<String, JsonNode>> fields = treeNode.fields();
String name = "";
// collect the tag name
Map.Entry<String, JsonNode> entry = fields.next();
name = entry.getKey();
// now that we have the tag name, parse it as a separate JSON object
JsonNode node = entry.getValue();
// get the values from the JSON
String description = node.get("tag_description").asText();
String category_id = node.get("tag_category_id").asText();
String tag_id = node.get("tag_id").asText();
return new Tag(name, category_id, description, tag_id);
}
}
I'm calling the method from a Spring Boot REST API endpoint, and my 'tag' model is a Spring entity:
'Tag' model:
#Entity
#JsonDeserialize(using = TagSerializer.class)
public class Tag {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
private String tag_category_id;
private String tag_description;
private String tag_id;
//JPA requires that a default constructor exists
//for entities
protected Tag() {}
public Tag(String name,
String tag_category_id,
String tag_description,
String tag_id) {
this.name = name;
this.tag_category_id = tag_category_id;
this.tag_description = tag_description;
this.tag_id = tag_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTag_category_id() {
return tag_category_id;
}
public void setTag_category_id(String tag_category_id) {
this.tag_category_id = tag_category_id;
}
public String getTag_description() {
return tag_description;
}
public void setTag_description(String tag_description) {
this.tag_description = tag_description;
}
public String getTag_id() {
return tag_id;
}
public void setTag_id(String tag_id) {
this.tag_id = tag_id;
}
public String toString() {
return "<Tag:[Name: " + this.name + "],[tag_category: "+
this.tag_category_id + "],[tag_description: "+
this.tag_description + "],[tag_id:"+this.tag_id+"]";
}
}
Spring Boot endpoint:
#PostMapping(value="/store", consumes = APPLICATION_JSON_VALUE)
public void tagJson(#RequestBody String json) {
// delete any existing tags
tagRepository.deleteAll();
//lets modify the json to make it look nicer
String modjson = "["+json+"]";
ObjectMapper mapper = new ObjectMapper();
try {
Tag[] tags = mapper.readValue(modjson, Tag[].class);
for (Tag t : tags)
tagRepository.save(t);
} catch (Exception exception) {
exception.printStackTrace();
}
}
If you are using Spring MVC consider explicitly declare desired type when referreing to #RequestBody and let the framework do the job for you
#PostMapping(value="/store", consumes = APPLICATION_JSON_VALUE)
public void tagJson(#RequestBody Map<String, Tag> json) {
// Do not mess with ObjectMapper here, Spring will do the thing for you
}
This isn't a direct answer but a guide in a possible direction, using Gson.
package test;
import java.util.Map;
import com.google.gson.Gson;
public class JsonTest {
public static void main(final String... args) {
new JsonTest().run();
}
public void run() {
final Gson gson = new Gson();
final Map<?, ?> result = gson.fromJson("{" +
" \"Sample\": {" +
" \"tag_description\": \"abc\"," +
" \"tag_category_id\": \"def\"," +
" \"tag_id\": \"ghi\"" +
" }," +
" \"Sample1\": {" +
" \"tag_description\": \"jkl\"," +
" \"tag_category_id\": \"mno\"," +
" \"tag_id\": \"pqr\"" +
" }" +
"}", Map.class);
System.out.println("Map size: " + result.size());
}
}
The resulting size is 2. The map entries are keyed Sample, Sample1, and the values are lists containing the nodes. You can see this using a debugger.
I have a json structure as shown below:
{
"clientId": 111,
"clientName": "abc",
"holder": [
{
"clientKey": "abc1",
"clientValue": {"hello" : "world"}
},
{
"clientKey": "abc2",
"recordValue": {}
}
]
}
I am deserializing my above JSON to my POJO using Jackson. Below is my POJO class where everything will get serialized.
import org.codehaus.jackson.annotate.JsonProperty;
public class DataRequest {
#JsonProperty("clientId")
private int clientId;
#JsonProperty("clientName")
private String clientName;
#JsonProperty("holder")
private List<ClientHolder> holder;
// getters and setters
public static class ClientHolder {
#JsonProperty("clientKey")
private String clientKey;
#JsonProperty("clientValue")
private Map<String, Object> clientValue;
// getters and setters
}
}
Is there any way I can have some sort of annotations in jackson that can do the validation while it is getting deserialized instead of doing the validations check after everything is deserialized? I want to validate below things:
clientId should be greater than zero.
clientName should never be null or empty string.
holder List should never be empty.
clientKey should never be null or empty string.
clientValue should never be null or empty as well.
Right now I am validating here:
private void validate(DataRequest request) {
if (request.getSchemaId() <= 0) {
// throw some exception
}
if (request.getClientName() == null || request.getClientName().isEmpty()) {
// throw some exception
}
// now I am not sure how should I do the validation for each
// clientKey and clientValue here efficiently
// if this is the only way we can do validations
}
You can consider using the Jackson Builder pattern support to build your model. Then you can put your validation code in the builder's build method. The build method will be called as a part of deserialisation. Here is a complete example with Jackson 2.X.
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class JacksonBuilderValidation {
final static String JSON = "{\n"
+ " \"clientId\": 111,\n"
+ " \"clientName\": \"abc\",\n"
+ " \"recordValue\": [\n"
+ " {\n"
+ " \"clientKey\": \"abc1\",\n"
+ " \"clientValue\": {\"hello\" : \"world\"}\n"
+ " },\n"
+ " {\n"
+ " \"clientKey\": \"abc2\",\n"
+ " \"clientValue\": {}\n"
+ " }\n"
+ " ]}";
#JsonDeserialize(builder = JacksonBuilderValidation.DataRequest.Builder.class)
static class DataRequest {
private int clientId;
private String clientName;
private List<ClientHolder> recordValue;
private DataRequest(Builder builder) {
this.clientId = builder.clientId;
this.clientName = builder.clientName;
this.recordValue = builder.recordValue;
}
#Override
public String toString() {
return "DataRequest{" +
"clientId=" + clientId +
", clientName='" + clientName + '\'' +
", recordValue=" + recordValue +
'}';
}
static class ClientHolder {
public String clientKey;
public Map<String, Object> clientValue;
#Override
public String toString() {
return "ClientHolder{" +
"clientKey='" + clientKey + '\'' +
", clientValue=" + clientValue +
'}';
}
}
#JsonPOJOBuilder(withPrefix = "")
static class Builder {
private int clientId;
private String clientName;
private List<ClientHolder> recordValue;
Builder clientId(int clientId) {
this.clientId = clientId;
return this;
}
Builder clientName(String clientName) {
this.clientName = clientName;
return this;
}
Builder recordValue(List<ClientHolder> recordValue) {
this.recordValue = recordValue;
return this;
}
DataRequest build() {
final DataRequest dataRequest = new DataRequest(this);
// write validation code here
System.out.println("Is record value empty? "
+ dataRequest.recordValue.isEmpty());
return dataRequest;
}
}
}
public static void main(String[] args) throws IOException {
final ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(JSON, DataRequest.class));
}
}
Output:
Is record value empty? false
DataRequest{clientId=111, clientName='abc', recordValue=[ClientHolder{clientKey='abc1', clientValue={hello=world}}, ClientHolder{clientKey='abc2', clientValue={}}]}
Google's cAdvisor API gives JSON output like this:
{
/system.slice/docker-13b18253fa70d837e9707a1c28e45a3573e82751f964b66d7c4cbc2256abc266.scope: {},
/system.slice/docker-747f797d19931b4ef33cda0c519f935b592a0b828d16b8cafc350568ab2c1d28.scope: {},
/system.slice/docker-bf947bfabf61cd5168bd599162cf5f5c2ea2350eece1ded018faebf598f7ee5b.scope: {},
/system.slice/docker-e8e02d508400438603151dd462ef036d59fada8239f66be8e64813880b59a77d.scope: {
name: "/system.slice/docker-e8e02d508400438603151dd462ef036d59fada8239f66be8e64813880b59a77d.scope",
aliases: [...],
namespace: "docker",
spec: {...},
stats: [...]
}
}
I would describe this as 4 same-typed JSON objects with variable/anonymous names, held in an anonymous object.
My first thought would just do something like mapper.readValue(response, Containers.class), where:
public class Containers extends BaseJsonObject {
#JsonProperty
public List<Container> containerList;
}
and
public class Container extends BaseJsonObject {
#JsonProperty
private String name;
#JsonProperty
public String[] aliases;
#JsonProperty
private String namespace;
#JsonProperty
private String spec;
#JsonProperty
public Stats[] stats;
}
But all of the variations on this I can think of yield the same result: some permutation of com.xyz.Containers#45c7e403[containerList=<null>] or com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "/system.slice/docker-13b18253fa70d837e9707a1c28e45a3573e82751f964b66d7c4cbc2256abc266.scope" (class com.xyz.Containers), not marked as ignorable (one known property: "containerList"])
at [Source: java.io.StringReader#3d285d7e; line: 1, column: 97] (through reference chain: com.xyz.Containers["/system.slice/docker-13b18253fa70d837e9707a1c28e45a3573e82751f964b66d7c4cbc2256abc266.scope"]), with ACCEPT_SINGLE_VALUE_AS_ARRAY = false .
I've tried:
mapper.readValue(response, Container[].class)
mapper.readValue(response, Containers.class)
mapper.readValues(jsonParser, Container.class)
As well as the following configurations:
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
How can I parse JSON objects with variable/anonymous names, held in a non-array? What is this called?
You can use the #JsonAnySetter annotation as follows and put the objects with variable names into a map of Map<String, Container>.
Here is an example:
public class JacksonVariableNames {
static final String JSON = "{\n"
+ " \"a\": {\n"
+ " \"value\": \"1\"\n"
+ " },\n"
+ " \"b\": {\n"
+ " \"value\": \"2\"\n"
+ " },\n"
+ " \"c\": {\n"
+ " \"value\": \"3\"\n"
+ " }\n"
+ "}";
static class Value {
private final String value;
#JsonCreator
Value(#JsonProperty("value") final String value) {this.value = value;}
#Override
public String toString() {
return "Value{" +
"value='" + value + '\'' +
'}';
}
}
static class Values {
private final Map<String, Value> values = new HashMap<>();
#JsonAnySetter
public void setValue(final String property, final Value value) {
values.put(property, value);
}
#Override
public String toString() {
return "Values{" +
"values=" + values +
'}';
}
}
public static void main(String[] args) throws IOException {
final ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(JSON, Values.class));
}
}
Output:
Values{values={a=Value{value='1'}, b=Value{value='2'}, c=Value{value='3'}}}