Converting java object having string json filed in to JSON - java

In our application one database table having some result stored as json as below:
---------------------------------------------------- -------------
content | other fields...
---------------------------------------------------- --------------
"{ \"key\":[\"value1\",\"value2\",\"value3\"]}" | 12 ...
I need to fetch and write into a result file , the content field of a set of records as a single json like:
(Expected)
[
{
"content": {
"key": [
"value1",
"value2",
"value3"
]
}
}
.....
]
In the orresponding java entity I put #JsonIgnore for all fields except content.
class Result{
//#JsonIgnore
//otherfields
....
#Column("content")
private String content;//the json string field
....
}
But when I read from db and write to file using:
ObjectWriter writer = new ObjectMapper().writer().withDefaultPrettyPrinter();
writer.writeValue(new File(outFile), entityList);
I got file as:
(Original)
[
{
"content" : "{ \"key\":[\"value1\",\"value2\",\"value3\"]}"
}
....
]
You may notice the issue. It take the jason field as a string and put as the value for the key "content", instead of a nested jason as expected

According to How can I include raw JSON in an object using Jackson? you can try to annotate the content with #JsonRawValue:
class Result {
#JsonRawValue
private String content;
}
This will output:
[ {
"content" : { "key":["value1","value2","value3"]}
} ]
which is semantically what you want. However, you expected the outout to be pretty formatted. This can be achieved by specifying a serializer, see also Convert JSON String to Pretty Print JSON output using Jackson :
class Result {
#JsonRawValue
#JsonSerialize(using = ToPrettyJsonSerializer.class)
private String content;
}
private static class ToPrettyJsonSerializer extends JsonSerializer<String> {
#Override
public void serialize(String string, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException {
Object json = new ObjectMapper().readValue(string, Object.class);
gen.writeObject(json);
}
}
This outputs:
[ {
"content" : {
"key" : [ "value1", "value2", "value3" ]
}
} ]
It is not exactly the format you expected, but getting close. Hope that helps.

I think, backlashes before quotation marks cause the problem and whole JSON data is treated as String instead of JSON object. You can try to remove backslashes before transforming JSON into object. One of the solutions I found is here: https://stackoverflow.com/a/19871960/1150795.

Try to use #JsonRawValue annotation.
How can I include raw JSON in an object using Jackson?

Related

Deserialize JSON with repeating keys to a list inside Java Object

If an "action" key-value pair is repeated, I want to append each associated "myObject" to a list as shown below. Is there a way to achieve this using GSON or JACKSON? Unfortunately, there is no option to edit the input JSON. If the ask is not clear, please let me know.
Input
[
{
myObject: {
name: "foo",
description: "bar"
},
action: "create",
},
{
myObject: {
name: "baz",
description: "qux"
},
action: "create",
},
];
Required Output
{
"action": "create",
"myObject": [
{
name: "foo",
description: "bar"
},
{
name: "baz",
description: "qux"
},
]
};
I am new to JSON parsing in Java and unfortunately haven't found a use case like mine on StackOverflow. I have tried configuring my ObjectMapper like so -
new ObjectMapper().configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
and using
#JsonAnySetter
annotation, but haven't gotten them to work yet.
You could solve this with two separate model classes, one for the original structure and one for the transformed one. For simplicity I call them OriginalModel and TransformedModel below, you should probably pick more meaningful names. The following code uses Gson but you can probably achieve something similar with Jackson as well.
class OriginalModel {
String action;
MyObjectData myObject;
}
class TransformedModel {
String action;
List<MyObjectData> myObject;
public TransformedModel(String action, List<MyObjectData> myObject) {
this.action = action;
this.myObject = myObject;
}
}
class MyObjectData {
String name;
String description;
}
If you declare these classes as nested classes you should make them static.
Then you can first parse the JSON data with the original model class, manually create the desired result structure using the transformed class and serialize that to JSON:
Gson gson = new Gson();
List<OriginalModel> originalData = gson.fromJson(json, new TypeToken<List<OriginalModel>>() {});
// Group MyObjectData objects by action name
// Uses LinkedHashMap to preserve order
Map<String, List<MyObjectData>> actionsMap = new LinkedHashMap<>();
for (OriginalModel model : originalData) {
actionsMap.computeIfAbsent(model.action, k -> new ArrayList<>())
.add(model.myObject);
}
List<TransformedModel> transformedData = new ArrayList<>();
for (Map.Entry<String, List<MyObjectData>> entry : actionsMap.entrySet()) {
transformedData.add(new TransformedModel(entry.getKey(), entry.getValue()));
}
String transformedJson = gson.toJson(transformedData);

Automatic deserialization from String to object for #RequestBody in Spring

I am having issues with serialization and as much as I have been looking around, I am not able to find a solution to this.
I have inside my #RestController something similar to this method for and endpoint:
public ResponseEntity<String>(#RequestBody RequestObject requestObject )
The RequestObject looks like this:
public class RequestObject {
private Driver driver;
private List<Tracks> tracks;
//constructors, getters and setters
}
public class Tracks {
private Enum1 name;
private List<Enum2> missions;
//constructors, getters and setters
}
So, the problem comes with the Listof Enum2. This is because what I receive from the JSON is not a list of strings, but rather a string, that I need to parse to convert to the values of Enum2
So Enum2 looks like this:
A,
B,
C;
And what I get from the request as a JSON is:
{
"driver" : {
"name" : "myname"
}
},
"tracks" : [
{
"name" : "madrid",
"missions" : "ABCCBA"
},
{
"name" : "barcelona",
"mission" : "CBBCA"
},
]
}
Thing is all deserialization works perfectly out of the box (including all nested enums) except for the List<Enum2> missions
I have managed to do a dirty trick, but I'd like to do it the proper way, which I understand would be to write some kind of deserializer that runs when the controller is fired and that does all that parsing and converting from the string to the list (this splitting code I do have it, but I don't know where to put it).
At the moment I am getting this
2021-09-13 21:02:34.924 WARN 99904 --- [nio-8080-exec-8] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.ArrayList<org.package.model.Enum2>` from String value (token `JsonToken.VALUE_STRING`); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.util.ArrayList<org.model.Enum2>` from String value (token `JsonToken.VALUE_STRING`)
at [Source: (PushbackInputStream); line: 13, column: 26] (through reference chain: org.model.RequestObject["missions"]->java.util.ArrayList[0]-org.model.Track["missions"])]
Is there a way to do this at all?
I found the solution. I had to mark Track as follows:
#JsonDeserialize(using = TrackDeserializer.class)
public class Track
and the write the TrackDeserializer:
public class TrackDeserializer extends JsonDeserializer<Track>{
#Override
public Track deserialize(JsonParser j, DeserializationContext ctxt) throws IOException{
JsonNode jsonNode = j.getCodec().readTree(jsonParser);
String name = jsonNode.get("name").asText();
final List<Enum2> enum = parseEnums(jsonNode.get("mission").asText());
return new Track(name, enum);
}
And parseEnums does the splitting and conversion to list.
Now TrackDeserializer is called for every Track with the given JSON in the question.
Probably, the simplest solution without using any Jackson annotation would be to use a custom setter method:
private void setMissions(String value) {
this.missions = convert_value_to_list_of_enums;
}
Jackson will invoke this method with values: ABCCBA and CBBCA.

Mapping Dynamic Json response(has changing attributes) using Jackson

I am trying to map Json response whose attributes changes if there is any error. For example:
valid response :
{
"Header":[{
//some data
}],
"Component":[{
//data
}],
"ServiceParameter":[{
//data
}]
}
error response:
{
"Header":[{
//some data
}],
"ErrorMessage":[{
//data
}],
"ServiceParameter":[{
//data
}]
}
How can I map(with Jackson if possible) component data or error message for the given response ?
I can only think of having both fields included in your POJO, like this:
class JsonResponse {
#JsonProperty("Headers")
private List<Header> headers;
#JsonProperty("Component")
private List<Component> components;
#JsonProperty("ErrorMessage")
private List<ErrorMessages> errorMessages;
#JsonProperty("ServiceParameters")
private List<ServiceParameters> serviceParameters;
// Getters and setters
}
You can then know whether it's an error or not by checking if the errorMessages list is empty or not.
Jackson is a very flexible JSON (de)serializer, so it's very likely there's a way to do it, but this one is definitely the easiest one!

Java GSON Json partial parsing

Say I have a JSON object representing an entity (can be any entity) like so:
{
"entity_id": "1",
"entity_name": "employee",
"entity_json": {
"employee_id": "e01",
"employee_name": "john",
"employee_phone_numbers": [
"1234567",
"8765433"
]
}
}
Note that entity_json can represent different entities having different structures as long as it is a valid JSON. For example, the following is another entity's representation:
{
"entity_id": "1",
"entity_name": "invoice",
"entity_json": {
"invoice_id": "1011",
"items": {
"item_id": "1",
"quantity": "3",
"price": "$100"
},
"date": "01-01-2020",
"customer": {
"id": "3",
"address": {
"street": "some_street",
"country": "CZ",
...
}
}
}
}
I want to be able to partially parse this JSON into an Entity POJO using Gson in Java. That is, I'll have an entity POJO like the one shown below:
public class Entity {
private String entity_id;
private String entity_name;
private String entity_json; // <-- entity_json is a String
// getters and setters
}
/*
* entity_json (for employee) = "{ \"employee_id\": \"1\", \"employee... }"
* entity_json (for invoice) = "{ \"invoice_id\": \"1011\", \"items... }"
*/
and I'm planning on performing any operation on entity_json using JsonPath.
Is there any way I can achieve this WITHOUT having to explicitly set entity_json in the JSON structure as a string with escapes?
Any help is appreciated here. Thanks!
You can avoid using a String for your entity_json by using Gson's JsonObject.
Here is my revised Entity class:
import com.google.gson.JsonObject;
public class MyEntity {
private String entity_id;
private String entity_name;
private JsonObject entity_json;
// getters and setters not shown
}
Then you can populate instances as follows:
MyEntity myEntityOne = new Gson().fromJson(JSON_ONE, MyEntity.class);
MyEntity myEntityTwo = new Gson().fromJson(JSON_TWO, MyEntity.class);
System.out.println(myEntityOne.getEntity_json());
System.out.println(myEntityTwo.getEntity_json());
In the above code, JSON_ONE and JSON_TWO are just strings containing the two sample JSONs from your question.
The console prints out the following (snipped for brevity):
{"employee_id":"e01","employee_name":"john","employee_phone_numbers":["1234567","8765433"]}
{"invoice_id":"1011","items":{"item_id":"1","quantity":"3","price":"$100"},"date":"01-01-2020"...
You can, of course, now use Gson to further manipulate each entity_json field as needed, since each one is itself a valid JSON object.

Jackson deserialization of class

Is it possible to deserialize the example below using Jackson?
public class A extends HashMap<String,String> {
//No other methods here for now
}
....
JSON Looks like this:
{"something":
{
"entry":
[
{"key":"one", "value":"avalue"},
{"key":"two", "value":"bvalue"}
]
}
}
...
At this time I'm getting error saying: Unrecognized Property Exception for entry.
Any help would be greatly appreciated.
First, your json is wrong, but I think I see what you're trying.
No it's not possible. HashMap<String,String> implies your object contains only top level string properties like:
{
"something": "value",
"somethingelse": "value2",
"someAdditionalThing": "value3"
}
To deserialize that you probably need to have a more strongly typed object. Jackson is falling over trying to turn:
{
"entry":
[
{"key":"one", "value":"avalue"},
{"key":"two", "value":"bvalue"}
]
}
Into a string.

Categories