Change one field from string to json object in jackson - java

I have a class where employeeDetails is being read from a data store as json string.
public class Employee {
private String name;
#JsonProperty("employeeDetails")
#JsonSerialize(???)
private String employeeDetailsBlob;
// getters and setters
}
Now I want to return employeeDetails(dynamic type) as an object in my response. I have changed the name using #JsonProperty and all the examples I have is for using #JsonSerialize(using = Employee.class).
So my response should look like this
{name: "foo", employeeDetails: { age: 21 }}
What I am getting is
{name: "foo", employeeDetails: "{ age: 21 }"}
I can add #JsonSerialize to my class but then I have to handle all the fields myself and do something like this in the overridden method.
jgen.writeFieldName("employeeDetails");
SerializedString serializedString = new SerializedString(empl.getEmployeeDetailsBlob());
jgen.writeRawValue(serializedString);
Is there a way I can do it using annotations and that too only on the field I want to change from json string to json object.

Adding #JsonRawValue did the trick.
public class Employee {
private String name;
#JsonProperty("employeeDetails")
#JsonRawValue
private String employeeDetailsBlob;
// getters and setters
}

Related

How to get Jackson mixin to handle an included type?

I'm using Jackson mixins to only serialize out specific fields.
My ObjectMapper is configured like so:
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.addMixIn(Person.class, SyncPerson.class);
mapper.addMixIn(TransactionLog.class, TransactionLogExport.class);
Here are the model classes paired with the JSON mixin objects that I'd like to export:
// Model class
public class Person {
private Long id;
private String email;
private String firstName;
private String lastName;
}
// Desired JSON format. Excludes 'id' field
public interface SyncPerson {
#JsonProperty("firstName")
String getFirstName();
#JsonProperty("lastName")
String getLastName();
#JsonProperty("email")
String getEmail();
}
// Model class
public class TransactionLog {
private long id;
private Integer version;
private Person person;
private Date date;
private EntityAction action;
}
// Desired JSON format. Excludes 'id' field, 'version', 'date'
public interface TransactionLogExport {
#JsonProperty("id")
String getId();
#JsonProperty("person")
Person person();
#JsonProperty("action")
EntityAction getAction();
}
Yet, my tests are showing that the person attribute of the TransactionLog isn't coming through.
#Test
public void testWriteValue() throws Exception {
Person person = new Person();
person.setEmail("a#c.com");
person.setFirstName("A");
person.setLastName("C");
TransactionLog log = new TransactionLog();
log.setId(0L);
log.setAction(EntityAction.CREATE);
log.setPerson(person);
log.setStartValue("start");
log.setEndValue("end");
log.setChanges("change");
String prettyJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(log);
System.out.println(prettyJson);
// Prints:
// {
// "id" : 0,
// "action" : "CREATE",
}
}
If I try the same test with a regular ObjectMapper mapper = new ObjectMapper(); instead of the mixin, then I see the full object exported, including the Person with email, names, etc. So something must be wrong with how I've configured the mixin... or else I'm misunderstanding something.
So can anyone help indicate what I could do to export out the subtype 'person' in my mixin?
Thanks!
Finally figured out the issue. The test now prints what we want:
{
“id” : 0,
“person” : {
“email” : “a#c.com”,
“firstName” : “A”,
“lastName” : “C”
},
“action” : “CREATE”
}
The mistake was in TransactionLogExport. It needs to say:
#JsonProperty("person")
Person getPerson();
Instead of:
#JsonProperty("person")
Person person();
I.e. the method needs to start with 'get'.

json serialization with a variable field as a string

I have a pojo that I am unmarshalling a REST response to. One of the fields ("variable value") is just a Json variable element (can be any form).
Is there a way to tell it to treat the field as a plain string for all cases instead of trying to deserialize to an object?
Here's a json obiect ("variable value" can be any form):
{"id":1, "variable value": {"name":"one", "age": 22, "data":{"key":"value"}}}
I would like to save this json as a class object using gson
public class SomeCommand {
private Long id;
private String data;
}
It sounds that you would like to parse the given JSON string to transform variable value into String object. You can achieve this by creating 2 classes - SomeCommandOriginal and SomeCommand as follows:
First, convert the JSON string to SomeCommandOriginal to map the value of variable value to JsonNode.
class SomeCommandOriginal {
private Long id;
#JsonProperty("variable value")
private JsonNode variableValue;
//general getters and setters
}
class SomeCommand {
private Long id;
private String data;
public SomeCommand(SomeCommandOriginal someCommandOriginal) {
super();
this.id = someCommandOriginal.id;
this.data = someCommandOriginal.variableValue.toString();
}
//general getters and setters
}
Second, initialize an instance of SomeCommand and pass someCommandOriginal as the argument of customized constructor:
ObjectMapper mapper = new ObjectMapper();
SomeCommandOriginal someCommandOriginal = mapper.readValue(jsonStr, SomeCommandOriginal.class);
SomeCommand someCommand = new SomeCommand(someCommandOriginal);
System.out.println(someCommand.getData());
Console output:
{"name":"one", "age": 22, "data":{"key":"value"}}
UPDATED
If you are using Gson, just modify the datatype of variableValue to be JsonObject and switch to #SerializedName annotation as follows:
class SomeCommandOriginal {
private Long id;
#SerializedName("variable value")
private JsonObject variableValue;
//general getters and setters
}
And then you can get the same result as well:
Gson gson = new Gson();
SomeCommandOriginal someCommandOriginal = gson.fromJson(jsonStr, SomeCommandOriginal.class);
SomeCommand someCommand = new SomeCommand(someCommandOriginal);
System.out.println(someCommand.getData());

change field name to lowercase while deserializing POJO to JSON using GSON?

I have a POJO class like this. I am deserializing my JSON to below POJO first..
public class Segment implements Serializable {
#SerializedName("Segment_ID")
#Expose
private String segmentID;
#SerializedName("Status")
#Expose
private String status;
#SerializedName("DateTime")
#Expose
private String dateTime;
private final static long serialVersionUID = -1607283459113364249L;
...
...
...
// constructors
// setters
// getters
// toString method
}
Now I am serializing my POJO to a JSON like this using Gson and it works fine:
Gson gson = new GsonBuilder().create();
String json = gson.toJson(user.getSegments());
System.out.println(json);
I get my json printed like this which is good:
[{"Segment_ID":"543211","Status":"1","DateTime":"TueDec2618:47:09UTC2017"},{"Segment_ID":"9998877","Status":"1","DateTime":"TueDec2618:47:09UTC2017"},{"Segment_ID":"121332121","Status":"1","DateTime":"TueDec2618:47:09UTC2017"}]
Now is there any way I can convert "Segment_ID" to all lowercase while deserializing? I mean "Segment_ID" should be "segment_id" and "Status" should be "status". Is this possible to do using gson? So it should print like this instead.
[{"segment_id":"543211","status":"1","datetime":"TueDec2618:47:09UTC2017"},{"segment_id":"9998877","status":"1","datetime":"TueDec2618:47:09UTC2017"},{"segment_id":"121332121","status":"1","datetime":"TueDec2618:47:09UTC2017"}]
if I change the "SerializedName" then while deserializing my JSON to POJO, it doesn't work so not sure if there is any other way.
You need to provide alternative names for deserialisation process and primary (value property) for serialisation.
class Segment {
#SerializedName(value = "segment_id", alternate = {"Segment_ID"})
#Expose
private String segmentID;
#SerializedName(value = "status", alternate = {"Status"})
#Expose
private String status;
#SerializedName(value = "datetime", alternate = {"DateTime"})
#Expose
private String dateTime;
}
Now, you can deserialise fields: Segment_ID, DateTime, Status and still be able to serialise as desired.

Can get field from inner json object with jackson?

I have json like that:
{
"somethingElse": "foobar",
"snils": {
"number": "123"
}
}
And class:
#Data
public class Documents {
private String snilsNumber;
private String somethingElse;
}
Can I easily map json to my class with annotation or something else?
You can use '#JsonRootName'
#Data
#JsonRootName(value = "snils")
#JsonIgnoreProperties(unknown = true)
public class Documents {
private String number;
}
You can deserialise it using one extra update method with JsonProperty annotation.
class Documents {
private String snilsNumber;
private String somethingElse;
#JsonProperty("snils")
private void unpackSnils(Map<String, Object> brand) {
this.snilsNumber = (String) brand.get("number");
}
// getters, setters, toString
}
See also:
Jackson nested values
unwrap inner json object using jackson

Using Jackson to convert to and from an object with nested JSON

I have an Entity class below with two String fields: name and description. The description field is to contain a raw JSON value e.g. { "abc": 123 }
#Getter
#Setter
public class Entity {
private String name;
#JsonRawValue
private String descriptionJson;
}
I've got simple test code below using Jackson to serialize and deserialize:
Entity ent = new Entity();
ent.setName("MyName");
ent.setDescriptionJson("{ \"abc\": 123 }");
// Convert Object to JSON string
String json = mapper.writeValueAsString(ent);
// Convert JSON string back to object
Entity ent2 = mapper.readValue(json, Entity.class);
When converting Object -> JSON the description string is nested because the #JsonRawValue is set:
{"name":"MyName","descriptionJson":{ "abc": 123 }}
However, when I call the Jackson mapper.readValue function to read the JSON string back into an entity object I get the exception:
com.fasterxml.jackson.databind.exc.MismatchedInputException:
Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
at [Source: (String)"{"name":"MyName","descriptionJson":{ "abc": 123 }}"; line: 1, column: 36] (through reference chain: com.test.Entity["descriptionJson"])
Given that the #JsonRawValue annotation exists, how would you recommend marshalling the created JSON string back into to Entity object? Is there another annotation I'm missing?
Thanks
#JsonRawValue is intended for serialization-side only, but in this problem you can do like this:
#Getter
#Setter
public class Entity {
private String name;
#JsonRawValue
private String descriptionJson;
#JsonProperty(value = "descriptionJson")
public void setDescriptionJsonRaw(JsonNode node) {
this.descriptionJson = node.toString();
}
}
This problem is repeated with
How can I include raw JSON in an object using Jackson?.
For one of my requirements I used field type as Map to store Json as it is. This way I was able to read the nested JSOn as Map and when I serialize object to JSON, it came up correctly. Below is the example.
Entity.java
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class Entity {
public int id=0;
public String itemName="";
public Map<String,String> owner=new HashMap<>();
}
Temp.java
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class Temp {
public static void main(String[] args){
ObjectMapper objectMapper= new ObjectMapper();
try {
Entity entity
=objectMapper.readValue(Temp.class.getResource("sample.json"), Entity.class);
System.out.println(entity);
String json=objectMapper.writeValueAsString(entity);
System.out.println(json);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Sample.json
{
"id": 1,
"itemName": "theItem",
"owner": {
"id": 2,
"name": "theUser"
}
}
You can use ObjectMapper from Jackson 2 as follows:
ObjectMapper mapper = new ObjectMapper();
String jsonStr = "sample json string"; // populate this as required
MyClass obj = mapper.readValue(jsonStr,MyClass.class)
try escaping the curly braces in the description json's value.
#JsonRawValue is intended only sor serializatio from docs:
Marker annotation that indicates that the annotated method or field should be serialized by including literal String value of the property as is, without quoting of characters.
To solve your problem you can try
public class Entity {
#Getter
#Setter
private String name;
private String descriptionJson;
#JsonRawValue
public String getDescriptionJson() {
return descriptionJson;
}
public void setJson(JsonNode node) {
this.descriptionJson = node.toString();
}
}

Categories