Serialize generic field from java object to json - java

I've a generic field in User.java. I want to use the value of T in json.
public class User<T> {
public enum Gender {MALE, FEMALE};
private T field;
private Gender _gender;
private boolean _isVerified;
private byte[] _userImage;
public T getField() { return field; }
public boolean isVerified() { return _isVerified; }
public Gender getGender() { return _gender; }
public byte[] getUserImage() { return _userImage; }
public void setField(T f) { field = f; }
public void setVerified(boolean b) { _isVerified = b; }
public void setGender(Gender g) { _gender = g; }
public void setUserImage(byte[] b) { _userImage = b; }
}
and mapper class is:
public class App
{
public static void main( String[] args ) throws JsonParseException, JsonMappingException, IOException
{
ObjectMapper mapper = new ObjectMapper();
Name n = new Name();
n.setFirst("Harry");
n.setLast("Potter");
User<Name> user = new User<Name>();
user.setField(n);
user.setGender(Gender.MALE);
user.setVerified(false);
mapper.writeValue(new File("user1.json"), user);
}
}
and the json output is :
{"field":{"first":"Harry","last":"Potter"},"gender":"MALE","verified":false,"userImage":null}
In the output, i want Name to be appeared in place of field. How do i do that. Any help?

I think what u ask is not JSON's default behavior. Field name is the "key" of the json map, not the variable name. U should rename the field or make some String process to do it.

private T field;
change the above to this:
private T name;

You need a custom serializer to do that. That's a runtime data transformation and Jackson has no support for data transformation other than with a custom serializer (well, there's wrapping/unwrapping of value, but let's not go there). Also, you will need to know in advance every type of transformation you want to apply inside your serializer. The following works:
public class UserSerializer extends JsonSerializer<User<?>> {
private static final String USER_IMAGE_FIELD = "userImage";
private static final String VERIFIED_FIELD = "verified";
private static final String FIELD_FIELD = "field";
private static final String NAME_FIELD = "name";
#Override
public void serialize(User<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
JsonProcessingException {
jgen.writeStartObject();
if (value.field instanceof Name) {
jgen.writeFieldName(NAME_FIELD);
} else {
jgen.writeFieldName(FIELD_FIELD);
}
jgen.writeObject(value.field);
jgen.writeStringField("gender", value._gender.name());
jgen.writeBooleanField(VERIFIED_FIELD, value._isVerified);
if (value._userImage == null) {
jgen.writeNullField(USER_IMAGE_FIELD);
} else {
jgen.writeBinaryField(USER_IMAGE_FIELD, value._userImage);
}
jgen.writeEndObject();
}
}

Related

Java GSON - serialize int as strings to json file

I have this Java class:
class Car {
int mileage;
int id;
}
When I tell gson to serialize it, it of course serializes it to:
{
"mileage": 123,
"id": 12345678
}
But what if I want to serialize it to:
{
"mileage": "123",
"id": "12345678"
}
Assuming changing my members from int to String, is not an option, is there a way to tell gson to serialize those int members as strings to the json file?
There are likely many ways to achieve what you desire.
I will share two ways.
FIRST - Using Custom Serialization
SECOND - Using JsonAdapter Annotation - More Simple
Using a custom serialization
public static class CarSerializer implements JsonSerializer<Car> {
public JsonElement serialize(final Car car, final Type type, final JsonSerializationContext context) {
JsonObject result = new JsonObject();
result.add("mileage", new JsonPrimitive(Integer.toString(car.getMileage())));
result.add("id", new JsonPrimitive(Integer.toString(car.getId())));
return result;
}
}
To call this, simply adapt your code or use the following code with a constructor
Car c = new Car(123, 123456789);
com.google.gson.Gson gson = new
GsonBuilder().registerTypeAdapter(Car.class, new CarSerializer())
.create();
System.out.println(gson.toJson(c));
The output should be
{"mileage":"123","id":"12345678"}
Full Code for Example 1:
public class SerializationTest {
public static class Car {
public int mileage;
public int id;
public Car(final int mileage, final int id) {
this.mileage = mileage;
this.id = id;
}
public int getId() {
return id;
}
public void setId(final int id) {
this.id = id;
}
public String getMileage() {
return mileage;
}
public void setMileage(final String mileage) {
this.mileage = mileage;
}
}
public static class CarSerializer implements JsonSerializer<Car> {
public JsonElement serialize(final Car car, final Type type, final JsonSerializationContext context) {
JsonObject result = new JsonObject();
result.add("mileage", new JsonPrimitive(Integer.toString(car.getMileage())));
result.add("id", new JsonPrimitive(Integer.toString(car.getId())));
return result;
}
}
public static void main(final String[] args) {
Car c = new Car(123, 123456789);
com.google.gson.Gson gson = new
GsonBuilder().registerTypeAdapter(Car.class, new CarSerializer())
.create();
System.out.println(gson.toJson(c));
}
}
Using a #JsonAdapter annotation
Use the JsonAdapter Annotation on the Car class
#JsonAdapter(CarAdapter.class)
public class Car {
public int mileage;
public int id;
}
Create the Custom Adapter
public class CarAdapter extends TypeAdapter<Car> {
#Override
public void write(JsonWriter writer, Car car) throws IOException {
writer.beginObject();
writer.name("mileage").value(car.getMileage());
writer.name("id").value(car.getId());
writer.endObject();
}
#Override
public Car read(JsonReader in) throws IOException {
// do something you need
return null;
}
}
To serialize, using this method, use something like this
Car c = new Car(123, 123456789);
Gson gson = new Gson();
String result = gson.toJson(c);
Printing result in this case should output
{"mileage":"123","id":"12345678"}
You may try it this way:
new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.registerTypeAdapter(Integer.class, (JsonSerializer<Integer>)
(integer, type, jsonSerializationContext) -> new
JsonPrimitive(String.valueOf(integer)))
.excludeFieldsWithoutExposeAnnotation().create();

Jackson : map nested object

Using jackson, i wonder if it's possible du map json to Java with nested Object that are not like the json structure.
Here an exemple of what i want to do.
Json :
{
a = "someValue",
b = "someValue",
c = "someValue"
}
Java :
public class AnObject {
#JsonProperty("a")
private String value;
//Nested object
private SomeObject;
}
public class SomeObject {
#JsonProperty("b")
private String value1;
#JsonProperry("c")
private String value2;
}
Is it possible ?
Use the JsonUnwrapped annotation:
#JsonUnwrapped
private final SomeObject someObject;
which unwrappes all of SomeObject's fields into the parent, resulting in the following when serializing:
{"a":"foo","b":"bar","c":"baz"}
Using ObjectMapper you can convert JSON string to Object.
Use JsonUnwrapped in your AnObject class over someObject field.
#JsonUnwrapped
private SomeObject someObject;
then read JSON string and convert it to AnObject.
ObjectMapper mapper = new ObjectMapper();
try {
AnObject anObject1 = mapper.readValue(jsonString, AnObject.class);
} catch (IOException e) {
e.printStackTrace();
}
First of all, this is a JSON object.
It's an object literal.
Second of all, that is not a valid formatted object literal.
The correct one is this:
{ "a" : "someValue", "b": "someValue", "c": "someValue"}
Next, as sayd in comments, you have to define your own deserializer.
Main:
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
String json = "{\"a\" : \"someValue\",\"b\" : \"someValue\",\"c\" : \"someValue\"}";
final ObjectMapper om =
new ObjectMapper();//
om.registerSubtypes(AnObject.class);
SimpleModule module = new SimpleModule();
module.addDeserializer(AnObject.class, new CustomDeserializer2());
om.registerModule(module);
AnObject ob = om.readValue(json, AnObject.class);
System.out.println(ob.getValue());
System.out.println(ob.getObject().getValue1());
System.out.println(ob.getObject().getValue2());
}
Deserializer:
public class CustomDeserializer2 extends StdDeserializer<AnObject> {
private static final long serialVersionUID = -3483096770025118080L;
public CustomDeserializer2() {
this(null);
}
public CustomDeserializer2(Class<?> vc) {
super(vc);
}
#Override
public AnObject deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode interNode = jp.getCodec().readTree(jp);
AnObject ob = new AnObject();
if (interNode.get("a") != null) {
ob.setValue(interNode.get("a").toString());
}
SomeObject obj = new SomeObject();
if (interNode.get("b") != null) {
obj.setValue1(interNode.get("b").toString());
}
if (interNode.get("c") != null) {
obj.setValue2(interNode.get("c").toString());
}
ob.setObject(obj);
return ob;
}
Model: Pay attention to #JsonProperty on A field
public class AnObject {
#JsonProperty("a")
private String value;
private SomeObject object;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public SomeObject getObject() {
return object;
}
public void setObject(SomeObject arg) {
object = arg;
}
public class SomeObject {
private String value1;
private String value2;
public String getValue1() {
return value1;
}
public void setValue1(String value1) {
this.value1 = value1;
}
public String getValue2() {
return value2;
}
public void setValue2(String value2) {
this.value2 = value2;
}
Bye

Issue With #JsonProperty on Method

I currently have my POJO class as such for deserializing a json source.
public class OpenBuilding extends Building {
#JsonProperty("BuildingPostCode")
#Override
public String getPostcode() {
return super.getPostcode();
}
}
Where the parent class is as such
public abstract class Buidling {
protected String postcode;
public String getPostcode() {
return this.postcode;
}
}
My issue is that the String postcode isn't getting mapped at all. It works when using the annotation on the field. However since it is an inherited field and I have other children of Building, which use different property names for the same data, I cannot have it implemented in that way.
For example:
public class DirectedBuilding extends Building {
#JsonProperty("Pseudo_PostCode")
#Override
public String getPostcode() {
return super.getPostcode();
}
}
Perhaps try defining a constructor with #JsonCreator.
class Parent {
private final String foo;
public Parent(final String foo) {
this.foo = foo;
}
public String getFoo() {
return foo;
}
}
class Child extends Parent {
#JsonCreator
public Child(#JsonProperty("foo") final String foo) {
super(foo);
}
#JsonProperty("foo")
public String getFoo() {
return super.getFoo();
}
}
public static void main(String[] args) throws Exception {
final ObjectMapper objectMapper = new ObjectMapper();
final Child toSerialize = new Child("fooValue");
// Serialize the object to JSON
final String json = objectMapper.writer()
.withDefaultPrettyPrinter()
.writeValueAsString(toSerialize);
// Prints { "foo" : "fooValue" }
System.out.println(json);
// Deserialize the JSON
final Child deserializedChild = objectMapper.readValue(json, Child.class);
// Prints fooValue
System.out.println(deserializedChild.getFoo());
}

Pass parameter to JsonSerialize

I have a rest service which send JSON. Before sending, I want to change one field, for example add n to price. n is a parameter, which comes from the client. How I can pass n to JsonSerializer without AOP and Reflection?
You can do with SimpleModule.
Create SimpleModule and create your Serializer object manually and pass parameter to constructor.
Here is example
Let say we have User model class like:
package com;
public class User {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
private String name;
private int number;
}
Here is custom Serializer class like :
package com;
public class UserSerializer extends JsonSerializer<User>{
private String n;
public UserSerializer(String n){
this.n = n;
}
#Override
public void serialize(User user, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
if(user != null){
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("name", "Hello, "+user.getName()+" : value of n : "+n);
jsonGenerator.writeEndObject();
}
}
}
Here is your main class :
public class Application {
public static void main(String[] args) throws Exception{
User user = new User();
user.setName("nitin");
String n = "1111"; //this value comes from API
SimpleModule module = new SimpleModule();
module.addSerializer(User.class, new UserSerializer(n));
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.registerModule(module);
String xml = xmlMapper.writeValueAsString(user);
System.out.println(xml);
}
}
and your out put should be like :
<User1 xmlns=""><name>Hello, nitin : value of n : 1111</name></User1>
If you want output in json, then use ObjectMapper.

Jackson custom serializer ignore #JsonIdentityInfo

I created a JacksonList class and JacksonListSerializer (extending JSonSerializer) for resolve a issue with collections and inheritance classes. Works fine.
The problem is that serializer ignore JsonIdentityInfo "rules". I have mapping exceptions like "Already had POJO for id (java.lang.Integer) .."
I put example below:
JacksonList.java
#JsonSerialize(using = JacksonListSerializer.class)
class JacksonList<E> extends ArrayList<E> {
private static final long serialVersionUID = 1L;
}
JacksonListSerializer.java
public class JacksonListSerializer extends JsonSerializer<JacksonList<?>> {
#Override
public void serialize(JacksonList<?> list, JsonGenerator generator, SerializerProvider provider)
throws IOException, JsonProcessingException {
generator.writeStartArray();
if (list != null) {
for (Object item : list) {
generator.writeObject(item);
}
}
generator.writeEndArray();
}
}
FirstItem.java
public class FirstItem {
private Son son;
public Son getSon() {
return son;
}
public void setSon(Son son) {
this.son = son;
}
}
Son.java
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#id", scope=Son.class)
public class Son {
public Son(String text) {
this.text = text;
}
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
Main.java
ObjectMapper mapper = new ObjectMapper();
FirstItem f1 = new FirstItem();
FirstItem f2 = new FirstItem();
JacksonList<FirstItem> jacksonList = new JacksonList<FirstItem>();
List<FirstItem> list = new ArrayList<FirstItem>();
Son son = new Son();
f1.setSon(son);
f2.setSon(son);
list.add(f1);
list.add(f2);
jacksonList.add(f1);
jacksonList.add(f2);
System.out.println(mapper.writeValueAsString(jacksonList));
System.out.println(mapper.writeValueAsString(list));
Output:
{"JacksonList":[{"FirstItem":{"id":1,"son":{"type":"com.Son","#id":1}}},{"FirstItem":{"id":2,"son":{"type":"com.Son","#id":1}}}]}
{"ArrayList":[{"id":1,"son":{"type":"com.Son","#id":1}},{"id":2,"son":1}]}
As you can see in the second case (print of ArrayList) the information of the Son class are not duplicated. But in the first case the object son is put two times.

Categories

Resources