I have a class with some attributes and in some cases I need to serialize only some of its fields and in other cases other fields. Let's say:
public class MyClass {
private String a;
private String b;
private String c;
private String d;
//constructor
public static MyClass fromString(final String string) {
Gson gson = new Gson();
MyClass obj = gson.fromJson(string, MyClass.class);
return obj;
}
public String toStringVersion1() {
final Gson gson = new GsonBuilder().create();
//here I want to serialize only a and b fields
}
public String toStringVersion2() {
final Gson gson = new GsonBuilder().create();
//here I want to serialize only c and d fields
}
}
I tried to use ExclusionStrategy from GSON, I defined a strategy for each version but I have to provide field names as parameter to strategy's constructor:
public class TestExclStrat implements ExclusionStrategy {
private Set<String> toBeSerialized;
public TestExclStrat(Set<String> fields) {
toBeSerialized = fields;
}
#Override
public boolean shouldSkipClass(Class<?> arg0) {
return false;
}
#Override
public boolean shouldSkipField(FieldAttributes f) {
return !toBeSerialized.contains(f.getName());
}
}
As far as I know, there is no way to get fieldNames at runtime in Java and I can't afford hardcoding name of the fields ("a", "b" for the first strategy and "c" and "d" for the second one). Any ideas?
Related
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();
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
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());
}
I use fasterxml to serialize/deserialize JSON
public class A {
String field;
B b;
}
public class B {
int n;
}
I want to get a JSON in format like this
{
"field": "abc",
"n": 123
}
Is it possible?
You can use Jackson annotation to provide a specific deserializer.
#JsonDeserialize(using = ADeserializer.class)
public class A {
private String field;
private B b;
// ...
}
A deserializer for your type should be like this
public class ADeserializer extends JsonDeserializer<A> {
#Override
public A deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
ObjectCodec codec = p.getCodec();
JsonNode node = codec.readTree(p);
String field = node.get("field").asText();
int n = node.get("n").asInt();
A a = new A();
B b = new B();
b.setN(n);
a.setField(field);
a.setB(b);
return a;
}
}
For serialization it's possible to use custom Serializer. That's it.
You can simply use #JsonUnwrapped. No custom serializers are needed:
public class A {
public String field;
#JsonUnwrapped
public B b;
}
public class B {
public int n;
}
Pay attention to the fields accessibility or it will not work.
There is no way to do that in Java.
Imagine I have two classes, MyClass and MyOtherClass. I've written a serializer for MyClass. Without it, trying to serialize MyOtherClass won't work (because MyClass isn't serializable without the serializer I've written).
package com.mycompany.javatest;
import com.google.gson.*;
import java.lang.reflect.*;
public class JavaTest {
static class MyClass {
private int someValue = 123;
}
static class MyOtherClass {
private MyClass mc = new MyClass();
}
static class MyClassSerializer implements JsonSerializer<MyClass> {
#Override
public JsonElement serialize(MyClass t, Type type, JsonSerializationContext jsc) {
JsonObject result = new JsonObject();
// (Doing some magic to serialize the object here...)
result.add("someValue", jsc.serialize(t.someValue));
return result;
}
}
static class MyOtherClassSerializer implements JsonSerializer<MyOtherClass> {
#Override
public JsonElement serialize(MyOtherClass t, Type type, JsonSerializationContext jsc) {
JsonObject result = new JsonObject();
result.add("mc", jsc.serialize(t.mc)); // <--- Will fail if not using the MyClassSerializer
return result;
}
}
public static void main(String[] args) {
GsonBuilder gb = new GsonBuilder();
gb.registerTypeAdapter(MyOtherClassSerializer.class, new MyOtherClassSerializer());
Gson gson = gb.create();
MyOtherClass object = new MyOtherClass();
String json = gson.toJson(object, MyOtherClass.class); // <--- MyClassSerializer.serialize MUST be invoked, or this will fail
}
}
My question is, how can I enforce that MyClassSerializer is registered when MyOtherClassSerializer is registered? The obvious answer is to just register both type adapters, but I'd like to know if there is a way to enforce registering both when registering MyOtherClassSerializer. One option is to only allow the type adapters to be accessed by a "register" method like this, but I don't like this solution. I still want the MyClassSerializer to be accessible.
public void registerMyOtherClassSerializer(GsonBuilder builder) {
builder.registerTypeAdapter(MyClass.class, new MyClassSerializer());
builder.registerTypeAdapter(MyOtherClass.class, new MyOtherClassSerializer());
}
Thoughts?
Thanks to Thomas Kläger. This is what I ended up doing:
package com.mycompany.javatest;
import com.google.gson.*;
import com.google.gson.reflect.*;
import com.google.gson.stream.*;
import java.io.*;
public class JavaTest {
static class MyClass {
private final int someValue = 123;
}
static class MyOtherClass {
private final MyClass mc = new MyClass();
}
public static void main(String[] args) {
GsonBuilder gb = new GsonBuilder();
gb.registerTypeAdapterFactory(new MyTypeAdapterFactory());
Gson gson = gb.create();
MyOtherClass object = new MyOtherClass();
String json = gson.toJson(object, MyOtherClass.class);
System.out.println(json);
}
static class MyTypeAdapterFactory implements TypeAdapterFactory {
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> tt) {
if (MyClass.class.isAssignableFrom(tt.getRawType())) {
return (TypeAdapter<T>) new MyClassAdapter();
}
return null;
}
private static class MyClassAdapter extends TypeAdapter<MyClass> {
#Override
public MyClass read(JsonReader reader) throws IOException {
throw new UnsupportedOperationException();
}
#Override
public void write(JsonWriter writer, MyClass t) throws IOException {
writer.beginObject();
writer.name("someValue");
writer.value(t.someValue); // (Doing some magic to serialize the object here...)
writer.endObject();
}
}
}
}