I'm trying to design a flatter structure for the output JSON file. How do I make it so that when I use gson.toJson() the member object will be serialized using the object's ID instead of serializing the whole thing?
For example:
class Foo {
public int id;
}
class Bar {
public Foo foo = new Foo();
public String data = "something";
}
I want gson.toJson(bar) to output something like this: {"foo": 1029, "data":"something"}.
Maybe something like this:
public class FooBarAdapter implements JsonSerializer<Bar> {
#Override
public JsonElement serialize(Bar bar, Type typeOfObj,
JsonSerializationContext context)
{
JsonObject obj = new JsonObject();
obj.addProperty("foo", bar.foo.id);
obj.addProperty("data", bar.data);
return obj;
}
}
and then when you want to serialize it:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Bar.class, new FooBarAdapter());
return gsonBuilder.create().toJson(bar);
Related
I have a list(list contains the requiredFields and this list can get the dynamic data from text file) and I have api response jsonData.
Now, I need to extract the data from api(jsonData) response, only the required fields(what list contained fields). All this need to be done using gson serializer
public class EDSJsonSerializer implements JsonDeserializer {
final list<String>; // list can be populated by reading data from text
file
//ex: list<Strin> is : [ab,bc]
#Override
public JsonElement deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
jsonElement => {"ab":"234234","bc":"wrwerewe","ww":"345fsd","456":"dfgdfg"}
final Map map = new HahMap();
map should contain only 2 elements {"ab":"234234","bc":"wrwerewe"}
map can be populated with list above given as keys and values from json passed
}
}
final String json = ""; // json is the api response string
final GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Map.class, new EDSJsonSerializer());
final Gson gson = builder.create();
final String map = gson.toJson(json);
it is not working as expected and not throwing any error/exception.
Please help me on this
Thanks,
Syamala.
Firstly 456 is not a valid Java class field name. It might be one reason of your problems even you might never use it from Json.
Secondly you migh better use ExclusionStrategy to decide which fields to de- & serialize.
In your case something like:
#RequiredArgsConstructor
public class ExcludeUnlistedFields implements ExclusionStrategy {
#NonNull
private Set<String> fieldsToInclude;
#Override
public boolean shouldSkipField(FieldAttributes f) {
// if you need to restrict to specific a class/classes
// add the checks here also
return ! fieldsToInclude.contains(f.getName());
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}
and use it like:
Set<String> fieldsToInclude = new HashSet<>(Arrays.asList("ab", "bc"));
ExclusionStrategy es = new ExcludeUnlistedFields(fieldsToInclude);
Gson gson = new GsonBuilder().setPrettyPrinting()
.addDeserializationExclusionStrategy(es).create();
When reading a JSON :
{"field":"value"}
into a String field :
public class Test {
private String field;
}
using Gson.fromJson it works as intended and the member String field gets the value "value".
My question is, is there a way to read the same JSON into a custom class so that the custom class object can be constructed with the String value? e.g.
public class Test {
private MyField<String> field;
}
public class MyField<T> {
private T value;
public MyField(T v) {
value = v;
}
}
The reason being the String class is final and cannot be extended, yet I don't want the JSON to be changed into this :
{"field":{"value":"value"}}
If there is a way to extend the String class, it is the best. Otherwise, will need a way for Gson to read string into a custom class that can be constructed by string. Something to do with writing a custom TypeAdapter?
You can use custom JsonDeserializer, JsonSerializer. Here is simple demo version:
static class MyFieldAsValueTypeAdapter<T> implements
JsonDeserializer<MyField<T>>, JsonSerializer<MyField<T>> {
private Gson gson = new Gson();
#Override
public MyField<T> deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context)
throws JsonParseException {
JsonObject obj = new JsonObject();
obj.add("value", json);
return gson.fromJson(obj, typeOfT);
}
#Override
public JsonElement serialize(MyField<T> src, Type typeOfSrc,
JsonSerializationContext context) {
return context.serialize(src.getValue());
}
}
public static void main(String[] args) {
GsonBuilder b = new GsonBuilder();
b.registerTypeAdapter(MyField.class , new MyFieldAsValueTypeAdapter());
Gson gson = b.create();
String json = "{\"field\":\"value1\"}";
Test test = gson.fromJson(json, Test.class);
}
Be careful with internal Gson gson = new Gson(). If you have some other setup, you will need to register it on internal version or pass default MyField deserializer/serializer to your custom implementation.
So I am currently using Jackson to deserialize JSON into complex java objects. Everything works well but I also have some fields such as:
{
"foo.bar.baz":"qux"
}
which correspond to java objects such as:
class Foo {
AnotherClass bar;
}
class AnotherClass {
String baz;
}
Jackson is unable to figure out that the dots correspond to inner objects. Is there a way to get Jackson to be able to deserialize even on flattened fields such as the field in my example?
No Jackson JSON library will not detect this as different object levels. You can use this instead:
{
"foo": {
"bar": {
"baz":"qux"
}
}
}
And you will have to create:
Class WrapperClass containing "foo" of type FooClass
Class FooClass containing "bar" of type BarClass
Class BarClass containing "baz" of type String
You can do something like that by using #JsonUnwrapped:
public class Wrapper {
#JsonUnwrapped(prefix="foo.bar.")
public AnotherClass foo; // name not used for property in JSON
}
public class AnotherClass {
String baz;
}
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(jsonString);
Iterator<String> iterator = root.fieldNames();
while (iterator.hasNext()) {
String fieldName = iterator.next();
if (fieldName.contains(".")) {
String[] items = fieldName.split("\\.");
if (items[0].equals("foo")) {
Foo foo = new Foo();
if (items[1].equals("bar")) {
AnotherClass bar = new AnotherClass();
foo.bar = bar;
if (items[2].equals("baz")) {
bar.baz = root.get(fieldName).asText();
}
}
}
}
}
I have the following classes:
public class Top {
private String key;
...
}
public class A extends Top {
private String aValue;
...
}
public class Complex {
private String field;
private List<Top> objects;
}
I want to deserialize a json String into a "Complex" class and specify that "objects" elements are of type "A".
I have tried 2 methods:
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(A.class, new InstanceCreator<A>() {
#Override
public A createInstance(Type arg0) {
return new A();
}
}) //method 1
.registerTypeHierarchyAdapter(A.class, new My_A_Adapter()) //method 2
.create();
Complex complexObject = gson.fromJson(json, Complex.class);
A = (A) complexObject.getObjects().get(0); // This throws ClassCastException
But the type of complexObject.getObjects().get(0) is "Top" so i cannot cast it to "A".
I do not want to parameterize the Complex class, (for ex. Complex) because i want to add more collections of generic objects in time...
What solution do I have ?
I have some Objects
public class MyObject {
private String name;
private String city;
public MyObject(String n, String c) {
name=n; city=c;}
public String getName() {return name;}
public String getCity() {return city;}
public void setName(String n) {name=n;}
public void setCity(String c) {city =c;}
}
And i have a custom serializer:
public class MySerializer implements JsonSerializer<MyObject> {
public JsonElement serialize(final MyObject myobj, final Type type, JsonSerializationContext context) {
JsonObject result = new JsonObject();
result.add("id", new JsonPrimitive(myobj.getName()));
return result;
}
}
Basically i just want to serilize only 1 of the 2 fields. this works great when do something like:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(MyObject.class, new MySerialize());
Gson gson = builder.create();
System.out.println(gson.toJson(new MyObject("test","something"));
however it gets a bit more complicated (and here is my question) when i have another object which is made up of "MyObject"s. How can i get the correct serializer to only serialize the one field of MyObject.
so i have another class:
public class SomeObject {
private String id;
private MyObject foo;
private MyObject bar;
...
}
and i have a custom serializer:
public JsonElement serialize(final SomeObject something, final Type type, JsonSerializationContext context) {
JsonObject result = new JsonObject();
Gson gson = new Gson();
result.add("id", new JsonPrimitive(something.getId()));
//here i need help
result.add("myobject1", new JsonPrimitive(gson.toJson(something.getFoo())));
return result;
}
I'm not sure if its best practice to create the GsonBuilder for "MyObject" inside the custom serializer for SomeObject is it?
ive tried something like:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(MyObject.class, new MySerialize());
builder.registerTypeAdapter(SomeObject.class, new SomeObjectSerializer());
Gson gson = builder.create();
System.out.println(gson.toJson(new SomeObject("id",new MyObject("test","something"),new MyObject("test2,"barrrrr"));
and i would exepct "{"id":"id","foo"{"id":"test"},"bar":{"id:"test2"}}
but that is not the case. bascially i want just the first field in a custom object whcih i have a seralizer for, but do i need to build that serializer inside another objects custom serializer? seems wrong, dunno why.
Note how you have access to the JsonSerializationContext in your custom JsonSerializer classes. You can call JsonSerializationContext#serialize(Object) and Gson will use a registered or default TypeAdapter to serialize that object and return a JsonElement which you can add to the outer JsonElement.