Parse JSON array with jackson where objects are strings? - java

I have a slightly odd question. I have created an object, let's call it a Profile, that successfully parses single JSON objects via an API that I call. There is also a multi-profile interface that will return a JSON array of Profile objects. The problem is, the multi-profile interface turns the sub objects into strings. Is there an automatic way I can tell jackson to parse these into objects?
Example of a single object:
{ "foo": "bar" }
Example of a multi object:
[ "{ \"foo\": \"bar\" }", "{ \"blah\": \"ugh\" }" ]
(Sorry can't use real data)
Notice that the sub objects are actually strings, with escaped quotes inside them.
For completeness, my code for the multi object parse looks like this:
ObjectMapper mapper = new ObjectMapper();
Profile[] profile_array = mapper.readValue(response.content, Profile[].class);
for (Profile p: profile_array)
{
String user = p.did;
profiles.put(user, p);
}
As I said, in the single-profile case, the Profile object parses. In the multi-profile case, I get this exception:
Exception: org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.xyz.id.profile.Profile, problem: no suitable creator method found to deserialize from JSON String

I suppose you'll have to create a custom deserializer and apply it to the every element of that array.
class MyCustomDeserializer extends JsonDeserializer<Profile> {
private static ObjectMapper om = new ObjectMapper();
#Override
public Profile deserialize(JsonParser jp, DeserializationContext ctxt) {
// this method is responsible for changing a single text node:
// "{ \"foo\": \"bar\" }"
// Into a Profile object
return om.readValue(jp.getText(), Profile.class);
}
}

There is no out-of-the-box support for "re-parsing" of embedded JSON-in-JSON content.
But this sounds like a possible request for enhancement (RFE)...

Have you tried using JAXB?
final ObjectMapper mapper = new ObjectMapper();
// Setting up support of JAXB
final AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
// make deserializer use JAXB annotations (only)
mapper.getDeserializationConfig().setAnnotationIntrospector(
introspector);
// make serializer use JAXB annotations (only)
mapper.getSerializationConfig().setAnnotationIntrospector(
introspector);
final StringReader stringReader = new StringReader(response);
respGetClasses = mapper.readValue(stringReader,
FooBarClass.class);
The above should get you started...
Also, you would need to mark each subclass like so:
#XmlElement(name = "event")
public List<Event> getEvents()
{
return this.events;
}

Related

Changing POJO structure on returning to API call

I have assigned values to my POJO class to return as response body to an API call. I want to change my POJO structure, like by making the POJO as value to an object on top of the POJO.
For example:
Home POJO:
{
int number;
String address;
}
Values are assigned to the POJO and I can send it as response body to my API call, but I want my response body to be:
{
output: {
number: 1,
address: "chennai"
}
}
I know that I can achieve this using JSON-Object or using a HashMap or a parent POJO (Note: I do not want to create a POJO Output just for this case).
Is there any other way to serialize the POJO like this using Jackson or any other methods for Java with Spring?
You can apply #JsonRootName annotation on your class specifying "output" as its value.
#JsonRootName("output")
public class MyPojo {
private int number;
private String address;
// all-args constructor, getters, etc.
}
But this annotation alone would not have any impact on serialization. In order to make to instruct Jackson to use it, we need to enable the serialization feature WRAP_ROOT_VALUE:
Feature that can be enabled to make root value (usually JSON Object but can be any type) wrapped within a single property JSON object, where key as the "root name", ...
Usage example:
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
System.out.println(
mapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(new MyPojo(1000, "foo"))
);
Output:
{
"output" : {
"number" : 1000,
"address" : "foo"
}
}

Get the fields values from an Object type object (Spring)

Under Java Spring boot, I have some returned object (of type Object) from a function having the following structure :
{id=5, name=Jasmin, description=Room Jasmin, idType=0, addedAt=2020-06-16T17:20:00.617+0000, modifiedAt=null, deleted=true, images=[string],
idBuilding=2, idFloor=4, idZone=3}
How to get the value of the id ?
I tried converting it to JSONObject but it's not working, also i tried the reflection approach :
Class<?> clazz = x.getClass();
Field field = clazz.getField("fieldName");
Object fieldValue = field.get(x);
But it's not working either returns null.
Thank you.
If you are unable to change the upstream function to return something more useful because it is from an external library or something, then creating a JsonNode (or similar) may be useful:
try {
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(x);
JsonNode jsonNode = mapper.readTree(json);
JsonNode idNode = jsonNode.get("id");
int id = idNode.asInt();
System.out.println("id = " + id);
}
catch (JsonProcessingException e) {
e.printStackTrace();
}
If the type is actually just 'Object', it does not implement Serializable and will need to be wrapped in class that does. Here is a great explanation: How to serialize a non-serializable in Java?
For reference:
https://www.baeldung.com/jackson-object-mapper-tutorial
First, create a simple POJO having a single property id Person.java
ObjectMapper mapper = new ObjectMapper();
// convertValue - convert Object of JSON to respective POJO class
GithubUsers githubUsers = mapper.convertValue(singleJsonData, Person.class);
If you fetch it using RestTemplate:
List<GithubUsers> returnValue = new ArrayList<>();
List<Object> listOfJsonData = restTemplate.getForObject("your-url", Object.class);
for (Object singleJsonData : listOfJsonData) {
ObjectMapper mapper = new ObjectMapper();
// convertValue - convert Object of JSON to respective POJO class
Person persons = mapper.convertValue(singleJsonData, Person.class);
returnValue.add(persons);
}
return returnValue;
From this, you can retrieve only id from the JSON Object.

Which class of Jackson library should I use to parse JSON data?

My JSON data:
[
"Gi 1/1",
{
"Shutdown":true,
"Speed":"force10ModeFdx",
"AdvertiseDisabled":0,
"MediaType":"dual",
"FC":"off",
"MTU":9600,
"ExcessiveRestart":false,
"PFC":0
}
]
Controlller class method:
public #ResponseBody void setSwitchPortInterfaceInfo(#RequestBody JsonNode jsonNode)
throws JsonProcessingException, IOException { }
When calling this method only "Gi 1/1" got parsed.
Which class do I need to pass as argument to parse complete JSON object?
The JSON Data represent in the question is not present in correct format. We need to convert it into proper version than only parse the Same.
Ideal way of declaring JSON structure is:
[{"key":"value"},{"key":"value"},{"key":"value"}]
ObjectMapper objectMapper = new ObjectMapper();
String strToParse = getString(); // put the string you want to parse
JsonNode node = objectMapper.readValue(strToParse, JsonNode.class);
System.out.println(node.get(0));
System.out.println(node.get(1));

Jackson/GSON: Apply Map to POJO

Let's say I have a POJO with quite a few fields. I also have a map with a bunch of properties that would map nicely to fields in the POJO. Now I want to apply the properties in the map to my POJO. How can I do this?
Jackson provides method new ObjectMapper().convertValue(), but that creates a fresh instance of the POJO. Do I really have to do something like this?
om = new ObjectMapper();
pojoMap = om.convertValue(pojo, Map.class);
pojoMap.putAll(properties);
pojo = om.convertValue(pojoMap, Pojo.class);
Isn't there an easier way?
As I have no experience with GSON and we also have it lying around here, how would I do that with GSON?
Yes, you can create an ObjectReader that will update an existing instance from the root JSON object rather than instantiating a new one, using the readerForUpdating method of ObjectMapper:
#Test
public void apply_json_to_existing_object() throws Exception {
ExampleRecord record = new ExampleRecord();
ObjectReader reader = mapper.readerForUpdating(record)
.with(JsonParser.Feature.ALLOW_SINGLE_QUOTES)
.with(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES);
reader.readValue("{ firstProperty: 'foo' }");
reader.readValue("{ secondProperty: 'bar' }");
assertThat(record.firstProperty, equalTo("foo"));
assertThat(record.secondProperty, equalTo("bar"));
}
public static class ExampleRecord {
public String firstProperty;
public String secondProperty;
}
You can also create a value-updating reader from an existing ObjectReader. The following declaration seems equivalent:
ObjectReader reader = mapper.reader(ExampleRecord.class)
.withValueToUpdate(record)
.with(/* features etc */);
Addition
The above didn't actually answer your question, though.
Since you don't have the changes you want to make to the record as JSON, but rather as a map, you have to finagle things so that Jackson will read your Map. Which you can't do directly, but you can write the "JSON" out to a token buffer and then read it back:
#Test
public void apply_map_to_existing_object_via_json() throws Exception {
ExampleRecord record = new ExampleRecord();
Map<String, Object> properties = ImmutableMap.of("firstProperty", "foo", "secondProperty", "bar");
TokenBuffer buffer = new TokenBuffer(mapper, false);
mapper.writeValue(buffer, properties);
mapper.readerForUpdating(record).readValue(buffer.asParser());
assertThat(record.firstProperty, equalTo("foo"));
assertThat(record.secondProperty, equalTo("bar"));
}
(btw if this seems laborious, serializing to a token buffer and deserializing again is in fact how ObjectMapper.convertValue is implemented, so it's not a big change in functionality)

Jackson readTree() ignore null and empty values

I have seen a few other posts that are similar to this so I apologise in advance if this has already been asked. However I can't get any of the solutions to work for me.
I am obtaining a chunk of JSON from a web service which I would then like to stash in Amazon's DynamoDB. However the JSON coming back has some null and empty values in it, which DynamoDB doesn't like.
So I would to remove these values from the JSON. I am currently using Jackson for JSON deserialising, via the readTree() method, as follows:
String sitesDataJson = webService.getData();
// create the object mapper and instruct it to skip null values
ObjectMapper mapper = new ObjectMapper();
mapper = mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
JsonNode jsonObj = null;
try {
jsonObj = mapper.readTree(sitesDataJson);
} catch (IOException e) {
e.printStackTrace();
}
The JSON I'm trying to read is as follows:
{
"domain_override":"",
"id":"106949",
"category_override":null,
"content_topic":null,
"modified_date":"2013-12-04 20:31:50",
"market":{
"brand_labels":{
"op":"allow_all"
},
"domains":null,
"blocked_contentattributes":{
"6":true
},
"blocked_creativetypes":{
"11":true,
"10":true,
"5":true
},
"brands":{
"ids":{
"5953":true,
"4644":true,
"8418":true,
"25480":true,
"95":true,
"5650":true
},
"op":"block"
},
"blocked_languages":{
},
"allow_unbranded_buyers":"0"
},
"type":"site",
"status":"Active",
"account_id":"100766",
"deleted":"0",
"v":"2",
"delivery_medium":"WEB",
"delivery_medium_id":"2",
"content_type_id":null,
"notes":null,
"platform_id":null,
"autorefresh_settings":{
},
"created_date":"2013-10-16 16:49:48",
"external_id":null,
}
Performing the readTree() with the NON_EMPTY or NON_NULL setting appears to make no difference. Doing a print of jsonObj using .toString() shows the empty and null values still there.
So am I going about this in the wrong way? Is there a better (or just correct!) way to do this?
I should also add that I have tried to do the same but using GSON, which I read removes these attributes by default. However that is not removing them either. So does this point to some oddity in the JSON being used?
But then I can't see how it can be as I've tried testing my code (both Jackson and GSON) with a very simple bit of JSON:
String json = "{"
+ "\"name\" : \"val1\","
+ "\"address\" : null"
+ "}";
But this still does not remove the address attribute when readTree() is used. Does this imply that the NON_NULL and NON_EMPTY settings have no effect on the readTree() method? Are they only in fact for when objects are serialised to JSON? Do I really need to read my JSON in to a POJO to be able to handle null or empty values?
Many thanks.
I am not sure 100% if the solution I am proposing will work for your case, but there is a way to use custom deserializers during the deserialization process.
StringDeserializer deserializer = new StringDeserializer();
SimpleModule module = new SimpleModule("StringDeserializerModule",
new Version(1, 0, 0, null));
module.addDeserializer(String.class, deserializer);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
Now you can add more custom desirializers for the properties you think null/ empty can be present and customize the deserialize() representation.
class StringDeserializer extends JsonDeserializer<String>
{
#Override
public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException
{
//Customize
}
}

Categories