GSON - Optional and required fields with naming policy - java

I need a function, that reads a json file and control the structur of the json file. Required fields should be defined. For that
I found a question that resolve a part of my problem Gson optional and required fields. But in this case the naming convention has not power any more. In my case I used following GsonBuilder:
this.gsonUpperCamelCase = new GsonBuilder()
.registerTypeAdapter(TestClass.class, new AnnotatedDeserializer<TestClass>())
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.create();
Every key-value from JSON, that is in this case the deserialized java object need to be lowercase. Otherwise it will throw JsonParseException.
For example I have this class:
class TestClass {
#JsonRequired
private String testName;
//getter & setter
Then this JSON-file can not be deserialized:
{
"TestName":"name"
}
But I want to get sure that UPPER_CAMEL_CASE is used in this case. Thx.

SerializedName is the annotation that can help you on this. Modifying the TestClass as below, you should be able to deserialize a JSON with TestName, tn, tn2 and when serializing, it always uses testName.
static class TestClass {
#JsonRequired
#SerializedName(value="testName", alternate = {"TestName", "tn", "tn2"})
private String testName;
}
public static void main(String[] args) {
Gson gsonUpperCamelCase = new GsonBuilder()
.registerTypeAdapter(TestClass.class,
new AnnotatedDeserializer<TestClass>())
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.create();
TestClass tc = gsonUpperCamelCase.fromJson("{\r\n" +
" \"TestName\":\"name\"\r\n" +
"}", TestClass.class);
System.out.println(tc.testName);
System.out.println(gsonUpperCamelCase.toJson(tc));
}
Output
name
{"testName":"name"}

Related

Gson Java - Child class from JSON

I have an abstract class for configuration files, which can be extended by lots of other classes. I managed to get the system working for writing it to JSON, but now I need the load function.
Here's the general Configuration class:
public class Configuration {
public boolean load(){
FileReader reader = new FileReader(this.getClass().getSimpleName() + ".json");
Gson gson = new Gson();
gson.fromJson(reader, this.getClass());
reader.close();
/** Doesn't give an error, but doesn't set any info to the child class */
}
public boolean save(){
FileWriter writer = new FileWriter(this.getClass().getSimpleName() + ".json");
Gson gson = new Gson();
gson.toJson(this, writer);
writer.close();
/** This all works fine. */
}
}
Here's an example of an extending class:
public class ExampleConfig extends Configuration {
private static transient ExampleConfig i = new ExampleConfig();
public static ExampleConfig get() { return i; }
#Expose public String ServerID = UUID.randomUUID().toString();
}
In my main class I would do:
ExampleConfig.get().load();
System.out.println(ExampleConfig.get().ServerID);
This does not give any errors, but neither is the class loaded from the JSON. It keeps outputting a random UUID even though I want to load one from the JSON file. I'm probably getting the wrong instance of the child class, but I'm out of ideas on how to fix this. (Using this in gson.fromJson(.....); does not work.
You're missing to assign a read value to your configuration instance. Java cannot support anything like this = gson.fromJson(...), and Gson can only return new values and cannot patch existing ones. The below is a sort of Gson hack, and please only use it if it's really a must for you. Again, I would strongly recommend you to redesign your code and separate your configuration objects and configuration readers/writers -- these are just two different things that conflict from the technical perspective. As a result of refactoring, you could have, let's say, once you get an instance of your configuration, just delegate it to a writer to persist it elsewhere. If you need it back, then just get an instance of a reader, read the configuration value and assign it to your configuration (configurations are singletons, I remember), like:
final ConfigurationWriter writer = getConfigurationWriter();
writer.write(ExampleConfig.get());
...
final ConfigurationReader reader = getConfigurationReader();
ExampleConfig.set(reader.read(ExampleConfig.class));
At least this code does not mix two different things, and makes the result of reader.read be explicitly read and assigned to your configuration singleton.
If you're fine to open the gate of evil and make your code work because of hacks, then you could use Gson TypeAdapterFactory in order to cheat Gson and patch the current configuration instance.
abstract class Configuration {
private static final Gson saveGson = new Gson();
public final void load()
throws IOException {
try ( final FileReader reader = new FileReader(getTargetName()) ) {
// You have to instantiate Gson every time (unless you use caching strategies) in order to let it be *specifically* be aware of the current
// Configuration instance class. Thus you cannot make it a static field.
final Gson loadGson = new GsonBuilder()
.registerTypeAdapterFactory(new TypeAdapterFactory() {
// A Gson way to denote a type since Configuration.class may not be enough and it also works with generics
private final TypeToken<Configuration> configurationTypeToken = new TypeToken<Configuration>() {
};
#Override
#SuppressWarnings("deprecation") // isAssignableFrom is deprecated
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// Checking if the type token represents a parent class for the given configuration
// If yes, then we cheat...
if ( configurationTypeToken.isAssignableFrom(typeToken) ) {
// The map that's artificially bound as great cheating to a current configuration instance
final Map<Type, InstanceCreator<?>> instanceCreators = bindInstance(typeToken.getType(), Configuration.this);
// A factory used by Gson internally, we're intruding into its heart
final ConstructorConstructor constructorConstructor = new ConstructorConstructor(instanceCreators);
final TypeAdapterFactory delegatedTypeAdapterFactory = new ReflectiveTypeAdapterFactory(
constructorConstructor,
gson.fieldNamingStrategy(),
gson.excluder(),
new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor)
);
// Since the only thing necessary here is to define how to instantiate an object
// (and we just give it an already-existing instance)
// ... just delegate the job to Gson -- it would think as if it's creating a new instance.
// Actually it won't create one, but would "patch" the current instance
return delegatedTypeAdapterFactory.create(gson, typeToken);
}
// Otherwise returning a null means looking up for an existing type adapter from how Gson is configured
return null;
}
})
.create();
// The value is still loaded to nowhere, however.
// The type adapter factory is tightly bound to an existing configuration instance via ConstructorConstructor
// This is actually another code smell...
loadGson.fromJson(reader, getClass());
}
}
public final void save()
throws IOException {
try ( final FileWriter writer = new FileWriter(getTargetName()) ) {
saveGson.toJson(this, writer);
}
}
private String getTargetName() {
return getClass().getSimpleName() + ".json";
}
private static Map<Type, InstanceCreator<?>> bindInstance(final Type type, final Configuration existingConfiguration) {
return singletonMap(type, new InstanceCreator<Object>() {
#Override
public Object createInstance(final Type t) {
return t.equals(type) ? existingConfiguration : null; // don't know if null is allowed here though
}
});
}
}
I hope that the comments in the code above are exhaustive. As I said above, I doubt that you need it just because of intention to have a bit nicer code. You could argue that java.util.Properties can load and save itself. Yes, that's true, but java.util.Properties is open to iterate over its properties by design and it can always read and write properties from elsewhere to anywhere. Gson uses reflection, a method of peeking the fields under the hood, and this is awesome for well-designed objects. You need some refactoring and separate two concepts: the data and data writer/reader.

Multiple GSON #SerializedName per field?

Is there any way in Gson to map multiple JSON fields to a single Java object member variable?
Let's say I have a Java class...
public class MyClass {
String id;
String name;
}
I want to use this single class with two different services. However, these two services differ in how they return their data...
{ "id": 2341, "person": "Bob" }
... and ...
{ "id": 5382, "user": "Mary" }
... respectively.
Is there any way to map both the "person" and "user" fields in the JSON string to the name field in the Java object?
(Note: I only ever need to convert from JSON string to Java object - never the other way around.)
In October 2015, Gson version 2.4 (changelog) added the ability to use alternate/multiple names for #SerializedName when deserializing. No more custom TypeAdapter needed!
Usage:
java
#SerializedName(value="name", alternate={"person", "user"})
kotlin
#SerializedName(value="name", alternate= ["person", "user"])
https://www.javadoc.io/doc/com.google.code.gson/gson/2.6.2/com/google/gson/annotations/SerializedName.html
for Kotlin fans
#SerializedName(value="name", alternate= ["person", "user"])
It is not supported to define multiple #SerializedName annotations to a field at Gson.
Reason: By default Deserialization is managed with a LinkedHashMap and the keys are defined by incoming json's field names (not the custom class's field names or the serializedNames) and there is a one to one mapping. You can see the implementation(how deserialization works) at ReflectiveTypeAdapterFactory class's inner class Adapter<T>'s read(JsonReader in) method.
Solution:
You can write a custom TypeAdapter which handles name, person and user json tags and maps them to name field of your custom class MyClass:
class MyClassTypeAdapter extends TypeAdapter<MyClass> {
#Override
public MyClass read(final JsonReader in) throws IOException {
final MyClass myClassInstance = new MyClass();
in.beginObject();
while (in.hasNext()) {
String jsonTag = in.nextName();
if ("id".equals(jsonTag)) {
myClassInstance.id = in.nextInt();
} else if ("name".equals(jsonTag)
|| "person".equals(jsonTag)
|| "user".equals(jsonTag)) {
myClassInstance.name = in.nextString();
}
}
in.endObject();
return myClassInstance;
}
#Override
public void write(final JsonWriter out, final MyClass myClassInstance)
throws IOException {
out.beginObject();
out.name("id").value(myClassInstance.id);
out.name("name").value(myClassInstance.name);
out.endObject();
}
}
Test case:
String jsonVal0 = "{\"id\": 5382, \"user\": \"Mary\" }";
String jsonVal1 = "{\"id\": 2341, \"person\": \"Bob\"}";
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MyClass.class, new MyClassTypeAdapter());
final Gson gson = gsonBuilder.create();
MyClass myClassInstance0 = gson.fromJson(jsonVal0, MyClass.class);
MyClass myClassInstance1 = gson.fromJson(jsonVal1, MyClass.class);
System.out.println("jsonVal0 :" + gson.toJson(myClassInstance0));
// output: jsonVal0 :{"id":5382,"name":"Mary"}
System.out.println("jsonVal1 :" + gson.toJson(myClassInstance1));
// output: jsonVal1 :{"id":2341,"name":"Bob"}
Examples about TypeAdapters.
Edit 2016.04.06 : As #Mathieu Castets has written at his answer, it is supported now. (That is the correct answer for this question.)
public abstract String[] alternate
Returns: the alternative names of
the field when it is deserialized Default: {}
For KOTLIN i used below but doesn't work
#SerializedName(value="name", alternate= ["person", "user"])
so i edited it and here it works fine!!
#SerializedName(value="name", alternate= arrayOf("person", "user"))

Parsing JSON String as simple as possible with GSON

Using GSON, how can i return a single key from a Multidimensional Json String?
Here is the Multidimensional Json String:
{"statusCode":0,"statusDescription":"OK","data":{"user":{"id":xxx,"company_id":xxx,"account_type":"5","enable_locations":true,"intuit_user_id":null,"nick_name":"xxx","is_owner":"1","enabled":"1"},"session_token":"xxx"}}
I want to return the "session_token" key value.
I'm trying this:
class app {
static class Response {
String session_token;
}
public void getSessionToken() {
String x = {"statusCode":0,"statusDescription":"OK","data":{"user":{"id":xxx,"company_id":xxx,"account_type":"5","enable_locations":true,"intuit_user_id":null,"nick_name":"xxx","is_owner":"1","enabled":"1"},"session_token":"xxx"}}
Response r = new Gson().fromJson(x, Response.class);
System.out.println(r.session_token);
}
}
But with this, my r.session_token returns null.
You would need to use Gson's JsonParser class directly and extract the data from the parse tree:
String myJsonString = "{\"name\":\"john\",\"lastname\":\"smith\"}";
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(myJsonString);
JsonObject jsonObject = element.getAsJsonObject();
String lastName = jsonObject.get("lastname").getAsString();
System.out.println(lastName);
That said, it's debatable whether this would save you any real time over:
(edited from comments below):
class App {
static class Response {
String lastname;
}
public static void main(String[] args) {
String myJsonString = "{\"name\":\"john\",\"lastname\":\"smith\"}";
Response r = new Gson().fromJson(myJsonString, Response.class);
System.out.println(r.lastname);
}
}
Gson will silently ignore the fact that there's more data in the JSON than you're interested in, and later on you might be interested in it, in which case it's trivial to add fields to your Response class.
Edit due to question changing:
You have a JSON object. It contains a field data whose value is an object. Inside that object you have a field session_token that you're interested in.
Either you have to navigate to that field through the parse tree, or you have to create Java classes that all will map to. The Java classes would resemble (at the bare minimum):
class Response {
Data data;
}
class Data {
String session_token;
}

Can I use Gson to serialize method-local classes and anonymous classes?

Example:
import com.google.gson.Gson;
class GsonDemo {
private static class Static {String key = "static";}
private class NotStatic {String key = "not static";}
void testGson() {
Gson gson = new Gson();
System.out.println(gson.toJson(new Static()));
// expected = actual: {"key":"static"}
System.out.println(gson.toJson(new NotStatic()));
// expected = actual: {"key":"not static"}
class MethodLocal {String key = "method local";}
System.out.println(gson.toJson(new MethodLocal()));
// expected: {"key":"method local"}
// actual: null (be aware: the String "null")
Object extendsObject = new Object() {String key = "extends Object";};
System.out.println(gson.toJson(extendsObject));
// expected: {"key":"extends Object"}
// actual: null (be aware: the String "null")
}
public static void main(String... arguments) {
new GsonDemo().testGson();
}
}
I would like these serializations especially in unit tests. Is there a way to do so?
I found Serializing anonymous classes with Gson, but the argumentation is only valid for de-serialization.
FWIW, Jackson will serialize anonymous and local classes just fine.
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
class MethodLocal {public String key = "method local";}
System.out.println(mapper.writeValueAsString(new MethodLocal()));
// {"key":"method local"}
Object extendsObject = new Object() {public String key = "extends Object";};
System.out.println(mapper.writeValueAsString(extendsObject));
// {"key":"extends Object"}
}
Note that Jackson by default won't access non-public fields through reflection, as Gson does, but it could be configured to do so. The Jackson way is to use regular Java properties (through get/set methods), instead. (Configuring it to use private fields does slow down the runtime performance, a bit, but it's still way faster than Gson.)

How can I get a Jackson mixin to work with private fields?

I was experimenting with Jackson 2.0 mixins to serialize a class with no annotations.
Simplified source code below. Note that I'm not using getters/setters, but it seemed like I should still be able to use mixins according to the documentation.
public class NoAnnotation {
private Date created;
private String name;
// make one with some data in it for the test
static NoAnnotation make() {
NoAnnotation na= new NoAnnotation();
na.created = new Date();
na.name = "FooBear";
return na;
}
// my Mixin "class"
static class JacksonMixIn {
JacksonMixIn(#JsonProperty("created") Date created,
#JsonProperty("name") String name)
{ /* do nothing */ }
}
// test code
public static void main(String[] args) throws Exception {
NoAnnotation na = NoAnnotation.make();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixInAnnotations(NoAnnotation.class, JacksonMixIn.class);
String jsonText = objectMapper.writeValueAsString(na);
System.out.println(jsonText);
}
}
When I run main I get
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class com.flyingspaniel.so.NoAnnotation and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.SerializationFeature.FAIL_ON_EMPTY_BEANS) )
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:51)
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:25)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:108)
at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:2407)
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:1983)
at com.flyingspaniel.so.NoAnnotation.main(NoAnnotation.java:49)
When I follow the instructions in the Exception and add a line
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
I no longer get an exception, but the result is an empty JSON object, {}.
If I make the fields public it works, but that is not something I want to do, as it's not a reasonable object design.
I'm guessing that I am leaving out a basic "setThis" step somewhere, but don't know what. How can I get mixins to work in this situation?
I figured it out. If you want to access private fields, you need to play with the Visibility by adding the following line:
objectMapper.setVisibilityChecker(VisibilityChecker.Std.defaultInstance()
.withFieldVisibility(Visibility.ANY));
For protected fields, you could also use Visibility.PROTECTED_AND_PUBLIC.
Full example
// test code
public static void main(String[] args) throws Exception {
NoAnnotation na = NoAnnotation.make();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixInAnnotations(NoAnnotation.class, JacksonMixIn.class);
objectMapper.setVisibilityChecker(VisibilityChecker.Std.defaultInstance()
.withFieldVisibility(Visibility.ANY));
String jsonText = objectMapper.writeValueAsString(na);
System.out.println(jsonText);
}
If you want use the annotation mixin the correct way to declare it is:
static class JacksonMixIn {
#JsonProperty Date created;
#JsonProperty String name;
}
When done in this way you can control the fields to serialize simply including/excluding them from the mix in.
As mentioned in your self-answer, changing the field visibility checker will resolve this situation. As an alternative to modifying the ObjectMapper, this can be done with a purely annotation-based solution by using the #JsonAutoDetect annotation:
#JsonAutoDetect(fieldVisibility = Visibility.ANY)
static class JacksonMixIn {
JacksonMixIn(#JsonProperty("created") Date created,
#JsonProperty("id") int id)
{ /* do nothing */ }
}

Categories