How to treat JSON Value as String Object in Java using ObjectMapper? - java

I am facing issue when converting Json to Java Object.
My "jsonText" field have json as value which i want to be placed in String. My custom Class hass following structure.
Class Custom{
#JsonProperty(value = "field1")
private String field1;
#JsonProperty(value = "jsonText")
private String jsonText;
}
Below is my code:
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(inputString);
String nodeTree = node.path("jsonText").toString();
List<PatientMeasure> measuresList =mapper.readValue(nodeTree,
TypeFactory.defaultInstance().constructCollectionType(ArrayList.class, CustomClass.class) );
Json to convert is :
"field1" : "000000000E",
"jsonText" : {
"rank" : "17",
"status" : "",
"id" : 0
}
Exception got:
org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: java.io.StringReader#3362f02f; line: 1, column: 108] (through reference chain: com.Custom["jsonText"])

You can try this:
JSONArray ar= new JSONArray(result);
JSONObject jsonObj= ar.getJSONObject(0);
String strname = jsonObj.getString("NeededString");

You can use a custom deserializer like this:
public class AnythingToString extends JsonDeserializer<String> {
#Override
public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
TreeNode tree = jp.getCodec().readTree(jp);
return tree.toString();
}
}
And then annotate your field to use this deserializer:
class Custom{
#JsonProperty(value = "field1")
private String field1;
#JsonProperty(value = "jsonText")
#JsonDeserialize(using = AnythingToString.class)
private String jsonText;
}

Related

How to set different type for variable in POJO than expected while deserializing json using gson

I want to convert JSON object back to Person object java, but field key can be String or String[]. So I am getting the following error: java.lang.IllegalStateException: Expected STRING but was BEGIN_ARRAY at path $.key
Example Json files:
{
"type":"set",
"key":"person",
"value":{
"name":"Elon Musk",
"car":{
"model":"Tesla Roadster",
"year":"2018"
},
"rocket":{
"name":"Falcon 9",
"launches":"87"
}
}
}
or
{"type":"get","key":["person","name"]}
JsonObject jsonObject = new JsonParser().parse(input.readUTF()).getAsJsonObject();
Gson gson = new Gson();
Person person = gson.fromJson(jsonObject, Person.class);
#Getter
#Setter
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class Person {
String type;
String key;
Value value;
}
May be you need to create custom deserializer like this:
class MyTypeModelDeserializer implements JsonDeserializer<MyBaseTypeModel> {
#Override
public MyBaseTypeModel deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
JsonElement jsonType = jsonObject.get("type");
String type = jsonType.getAsString();
MyBaseTypeModel typeModel = null;
if("type1".equals(type)) {
typeModel = new Type1Model();
} else if("type2".equals(type)) {
typeModel = new Type2Model();
}
// TODO : set properties of type model
return typeModel;
}
}

Deserialize JSON array to Map using Jackson

I have JSON structured like:
{
"id" : "123",
"name" : [ {
"stuff" : [ {
"id" : "234",
"name" : "Bob"
}, {
"id" : "345",
"name" : "Sally"
} ]
} ]
}
That I want to map to the following data structure:
MyInterface1
#Value.Immutable
#JsonSerialize(as = ImmutableMyInterface1.class)
#JsonDeserialize(as = ImmutableMyInterface1.class)
public interface MyInterface1 {
String id();
#JsonDeserialize(using = MyInterface1Deserializer.class)
List<MyInterface2> name();
}
MyInterface2
#Value.Immutable
#JsonSerialize(as = ImmutableMyInterface2.class)
#JsonDeserialize(as = ImmutableMyInterface2.class)
public interface MyInterface2 {
#JsonDeserialize(using = StuffDeserializer.class)
Map<String, MyInterface3> stuff();
}
MyInterface3
#Value.Immutable
#JsonSerialize(as = ImmutableMyInterface3.class)
#JsonDeserialize(as = ImmutableMyInterface3.class)
public interface MyInterface3 {
String id();
String name();
}
I'm using an ObjectMapper with readValue(stringWithJson,MyInterface1.class) to map this JSON to MyInterface1, which should continue down the chain to MyInterface3. This setup was working when I was using a List in MyInterface2, i.e. List<MyInterface3> name();
However, I want this to be a map instead of a list, ideally with "id" from the inner JSON as the key. This would allow me to get values with the following syntax:
MyInterface1.get(0).MyInterface2.get("id1").name();
The problem is that when attempting to create a custom StuffDeserializer.class, I'm getting the error:
Can not deserialize instance of com.foo.ImmutableMyInterface2$Json out of START_ARRAY token
when trying to do:
public Map<String, MyInterface3> deserialize(JsonParser jsonParser, DeserializationContext ctxt)
throws IOException {
MyInterface2 foo = Unmarshaller.OBJECT_MAPPER.readValue(jsonParser, MyInterface2.class); // error here
...
I think this is because Jackson is expecting "stuff" to be a List 'cause of the JSON array. What's the best way to deserialize this JSON to a map that uses values from the inner JSON as a key?
I would create a custom JsonDeserializer to map id and name into a map:
public class StringHashMapValueDeserializer extends JsonDeserializer<HashMap<String, String>>{
#Override
public HashMap<String, String> deserialize(JsonParser parser, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
HashMap<String, String> ret = new HashMap<String, String>();
ObjectCodec codec = parser.getCodec();
TreeNode node = codec.readTree(parser);
if (node.isArray()){
for (JsonNode n : (ArrayNode)node){
JsonNode id = n.get("id");
if (id != null){
JsonNode name = n.get("name");
ret.put(id.asText(), name.asText());
}
}
}
return ret;
}
}
And then I would create simple beans with annotating stuff property with the deserializer:
#Getter
#Setter
public class Name {
#JsonDeserialize(using = StringHashMapValueDeserializer.class)
Map<String, String> stuff;
#Override
public String toString() {
return "Name [stuff=" + stuff + "]";
}
}
Outer type:
#Getter
#Setter
public class OuterType {
String id;
List<Name> name;
#Override
public String toString() {
return "OuterType [id=" + id + ", stuff=" + name + "]";
}
}
Deserialization:
ObjectMapper mapper = new ObjectMapper();
OuterType response;
response = mapper.readValue(json, OuterType.class);
System.out.println(response);
System.out.println(response.getName().get(0).getStuff().get("234"));
console output:
OuterType [id=123, stuff=[Name [stuff={234=Bob, 345=Sally}]]]
Bob
Hope it helps.

Map JSON to pojo using Jackson for List that have different parameters

JSON FORMAT:
[
{
"0":
{
"cast":"",
"showname":"woh pagle",
"type":"Episodes"
},
"video":[
{
"src":"video.mp4"
},
{
"DRM":"False"
}
]
}
]
Here problem is I am getting below exception:
org.codehaus.jackson.map.JsonMappingException: Can not deserialize
instance of java.util.ArrayList out of START_OBJECT token at [Source:
java.io.StringReader#1c9ca1; line: 1, column: 55617] (through
reference chain:
com.apalya.myplex.valueobject.ThirdPartyContentDetailsArray["video"])
My pojo classes are :
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonProperty("0")
private ThirdPartySubContentDetails subContent;
#JsonProperty("video")
private List<ThirdPartySubContentVideoInfo> video;
My Sub class pojo is :
private String src;
#JsonIgnore
#JsonProperty("DRM")
private String drm;
Please help me to write a pojo for that video list.
Your json starts as an array and not as an Object. The important part to change is how the Objectmapper should generate your json. For returning a List you need to do it this way:
List<FirstJson> jsonList = mapper.readValue(json, new TypeReference<List<FirstJson>>(){});
Here is my short working test I implement locally:
public static void main(String[] args) {
String json = "[{\"0\":{\"cast\":\"\",\"showname\":\"wohpagle\",\"type\":\"Episodes\"},\"video\":[{\"src\":\"video.mp4\"},{\"DRM\":\"False\"}]}]";
ObjectMapper mapper = new ObjectMapper();
List<FirstJson> jsonList = mapper.readValue(json, new TypeReference<List<FirstJson>>(){});
System.out.println(jsonList.toString());
}
The first part of your JsonArray in Pojo.(Named it FirstJson)
public class FirstJson{
#JsonProperty("0")
private FirstJson subContent;
private String cast;
private String showname;
private String type;
#JsonProperty("video")
private List<Video> videos;
//getter/setter
And the Video Pojo:
public class Video {
private String src;
#JsonProperty("DRM")
private String drm;
//getter/setter
Just a sidenote: If you declare your pojos in the same class file, the classes should be static. public static class FirstJson
According to the JSON structure described in the question, the following should be the POJOs:
public class MainPojo
{
#JsonProperty("0")
private ThirdPartySubContentDetails subContent;
#JsonProperty("video")
private List<ThirdPartySubContentVideoInfo> video;
// Getters and Setters for subContent and video
}
class ThirdPartySubContentDetails
{
private String cast;
private String showName;
private String type;
// Getters and Setters for cast, showName and type
}
#JsonIgnoreProperties(ignoreUnknown = true)
class ThirdPartySubContentVideoInfo
{
#JsonProperty("src")
private String src;
#JsonProperty("DRM")
private String drm;
// Getters and Setters for src and drm
}
You should call the deserializer method as follows:
List<MainPojo> list = new ObjectMapper().readValue(json, new TypeReference<List<MainPojo>>(){});

Why does jsonParser.getCodec().readTree(jsonParser).asText() return an empty String for me?

I wrote an REST service to ingest metadata from post requests. I am using spring-data-elasticsearch, and I made a custom Metadata Object to deserialize Json into that looks like this:
#Document(indexName = "metadata_v1", type = "metadata")
public class Metadata {
#Id
private String id;
#Field(type = FieldType.String)
private String uuid;
#Field(type = FieldType.String)
private String userId;
#Field(type = FieldType.Date, format = DateFormat.basic_date_time)
private Date date = null;
#Field(type = FieldType.String)
private String classification;
#Field(type = FieldType.Nested)
private List<NumericKeyValue> numericKeyValue;
#Field(type = FieldType.Nested)
private List<TextKeyValue> textKeyValue;
with a bunch of getters and setters.
It works fine for all of its fields except numericKeyValue and textKeyValue Json Arrays. I couldn't send those in via post request, and realized I needed to write a deserializer. I did that for numericKeyValue, and as far as I've read, it's supposed to look like this:
public class NumericKeyValueJsonDeserializer extends JsonDeserializer<List<NumericKeyValue>>{
#Override
public List<NumericKeyValue> deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
TypeReference<List<NumericKeyValue>> typeRef = new TypeReference<List<NumericKeyValue>>(){};
ObjectMapper mapper = new ObjectMapper();
JsonNode root = jp.getCodec().readTree(jp);
String numericKeyValue = root.get("numericKeyValue").asText();
return mapper.readValue( numericKeyValue, typeRef);
}
}
And I added
#JsonDeserialize(using = NumericKeyValueJsonDeserializer.class)
to the field declaration in my Metadata class.
However, after a lot of testing, I have come to realize that the JsonNode root not only doesn't contain "numericKeyValue", but gives me a completely empty string when I invoke root.asText().
I have been using Postman to send in a post request to my endpoint
#RequestMapping(value="/metadata_v1/ingest", method=RequestMethod.POST, consumes="application/json")
public #ResponseBody Metadata createEntry(#RequestBody Metadata entry){
repository.save(entry);
return entry;
}
containing the following Json:
{
"numericKeyValue":
[
{
"key": "velocity",
"value": 55.5
},
{
"key": "angle",
"value": 90
}
]
}
My mapping looks like this:
"numericKeyValue" : {
"type" : "nested",
"properties" : {
"key" : {"type" : "string"},
"value" : {"type" : "double"}
}
}
I can show more things if needed. I think I will be fine if I can just get the JSON I send in Java somehow, perhaps as a String. I've been getting empty Strings that result in null pointer exceptions and when I tried String numericKeyValue = jp.getText() the String was just the current token of "[", which I guess at least isn't an empty String, but still doesn't help me.
Any help or advice is much appreciated.
In your case the value for numericKeyValue is an array. You should replace the following line:
String numericKeyValue = root.get("numericKeyValue").asText();
with:
if ( root.isArray() ){
// loop trough array
for (final JsonNode node : root){
String numericKeyValue = node.get("numericKeyValue").asText();
// then build the list to be returned
}
}

Convert JSON to a Java object with a calculated field

I have this JSON object:
[
{
"field1": "xxxxx",
"field2": "vvvvvv",
"field3": "cccccc",
"field4": "zzzzzzz"
},
{
"field1": "aaaaa",
"field2": "ssssss",
"field3": "dddddd",
"field4": "ffffff"
}
]
I'm using FasterXML's Jackson library to deserialize this JSON to my class Foo. This one has this structure:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Foo {
private String id;
#JsonProperty("field1")
private String customField1;
#JsonProperty("field2")
private String customField2;
#JsonProperty("field3")
private String customField3;
#JsonProperty("field4")
private String customField4;
................
}
I would like to calculate value of field id at deserialize time. This value is the result of concatenating customField4 with customField3. Is possible to perform this kind of operation or do I need to pass this value into my JSON?
Ok guys, solution is to set a custom
#JsonDeserialize(using = EntityJsonDeserializerCustom.class)
in this way I've created a generic static class with only fields returned by json an then I override deserialize method to return me my object with calculated field
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonDeserialize(using = EntityJsonDeserializerCustom.class)
public class Foo {
private String id;
#JsonProperty("field1")
private String customField1;
#JsonProperty("field2")
private String customField2;
#JsonProperty("field3")
private String customField3;
#JsonProperty("field4")
private String customField4;
................
}
public class EntityJsonDeserializerCustom extends JsonDeserializer<Foo> {
#Override
public Foo deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
InnerFoo innerFoo = jp.readValueAs(InnerFoo.class);
Foo foo = new Foo();
foo.setField1(innerFoo.field1);
foo.setField2(innerFoo.field2);
foo.setField3(innerFoo.field3);
foo.setField4(innerFoo.field4);
foo.setId(innerFoo.field4 + innerFoo.field3);
return foo;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static class InnerFoo {
#JsonProperty("field1")
private String customField1;
#JsonProperty("field2")
private String customField2;
#JsonProperty("field3")
private String customField3;
#JsonProperty("field3")
private String customField4;
}
}
In this way I solve my problem, I hope this is helpfully for community :D

Categories

Resources