Deserializing JSON containing invalid class identifiers - java

In Java, using Jackson, I want to deserialize JSON that looks something like this:
{
"123_ABC": {
"XYZ": 768,
"123_DATA": {
"123_DEF": "",
"123_ACT": "ZAC",
"123_PAG": {
"123_PAG_A": 1,
"123_PAG_B": 1
}
}
}
}
You all know that identifiers starting with a number are invalid in Java (and every programming language I ever heard of.)
I already know how to use #JsonProperty to translate field names, but handling class names is outside my knowledge.
If I define classes corresponding to the structure of the JSON, but with valid class names, is there a way to use Jackson annotations to map the invalid class id in the JSON to my valid class names?

I think #JsonProperty should be good to deserialize this.
Let's create a wrapper class that will have 123_ABC as a property of class ValidClass.
class Wrapper {
#JsonProperty("123_ABC")
private ValidClass validName;
}
Now, when you serialize, it will create JSON like this (or can be deserialized using that)
{ "123_ABC":{ //PROPERTIES OF ValidClass HERE } }
Similarly, you can have different properties in further inner classes.
In case if you to support 123_ABC only for deserialization and serialize with correct field names, you can do like this
#JsonAlias("123_ABC")
private ValidClass validName;
it will serialize to following.
{"validName": {//properties}}
but deserialization can be done using both
{"validName": {//properties}}
{"123_ABC": {//properties}}
In case, if keys keep changing, I would suggest to deserialize them in Map.

Related

Change naming of attributes in json without #JsonProperty

I have to convert my json from camelCase to kebab-case.
Example:
My Json:
{
"disclaimerConfirmed" : true
}
And I need:
{
"disclaimer-confirmed" : true
}
I cannot use #JsonProperty because it rename this atributes permanently. I am looking for something which will consume Json (can be as String) and returns modified json(as String).
Jackson supports naming strategies so you could read the input String to map (with camelCase strategy) and then write the map back to a String (with kebab-case which is natively supported );
Specific method you need to switch these conventions in ObjectMapper without annotations is:
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.*);
You can have different serializers for different cases or you can create pojo with #JsonProperty and use those where-ever required.
For example,
class A {
private String disclaimerConfirmed;
}
class AkebabCase {
#JsonProperty("disclaimer-confirmed")
private String disclaimerConfirmed;
}
So, if you want to serialize to kebab-case you can use converters to convert A to AkebabCase and then serialize.

Issue using jackson to parse JSON containing variable that equals the "final" keyword in java

I am using jackson to parse json files to java objects.
This requires me to create java objects matching the json files. However currently I have a json file looking like this:
{
"name": "myName",
"final": "whatever"
}
Normally I would create a matching class in java:
class MyClass {
String name;
String final;
}
However the problem here is that 'final' is a java keyword and can't be used as a Java variable name. What would be the best way to solve this (trying to avoid manual parsing as the above is just a simplified version of the real situation).
You can use #JsonProperty annotation to tell jackson how to serialize/deserialize your custom fields. final is a special keyword in Java so you can use this annotation and name field whatever is valid :
class MyClass {
String name;
#JsonProperty("final")
String someName;
}

Object serialization to json, certain fields only

I have a large nested object. I want to serialise this object in the JSON string, however I need only certain fields to be included. Problem here is that fields could change very frequently and I want to build it in a way that could help me easy include or exclude fields for serialisation.
I know that I can write a lot of code to extract certain fields and build JSON "manually". But I wonder if there are any other elegant way to achieve similar outcome but specifying a list of required fields?
For example having following object structure I want include only id and name in the response:
class Building {
private List<Flat> flats;
}
class Flat {
private Integer id;
private Person owner;
}
class Person {
private String name;
private String surname;
}
Json:
{
"flats" : [
{
"flat":
{
"id" : "1",
"person" : {
"name" : "John"
}
}
}
]
}
You can use gson for serializing/deserializing JSON.
Then you can include the #Expose annotation to use only the fields you require.
Be sure to also configure your Gson object to only serialize "exposed" fields.
Gson gson = GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Alternative:
You can actually do it the inverse way, marking fields which will not be exposed. You can do this with the transient keyword.
So whatever you want to ignore just add transient to it. Here's how it works on gson.
PS: This works on most Java JSON serializers too.
Using com.fasterxml.jackson.annotation.JsonIgnore is another way to achieve this.
import com.fasterxml.jackson.annotation.JsonIgnore;
class Person {
private String name;
#JsonIgnore
private String surname;
}
It will ignore the surname when the parser converts the bean to json.
Similar annotation will be available in other json processing libraries.
If using Gson, study how to use ExclusionStrategy & JsonSerializer.
Using those is a more flexible way to control serialization since it allows to decide per serialization what to serialize.
Using annotations requires later to add / remove those annotations from fields if there is a need to change what to serialize.
In the case of your example the latter might be more appropriate.
This question might be good startpoint
serialize-java-object-with-gson

how to convert json property like "oData.type" to java object [duplicate]

What is the basic purpose of #SerializedName annotation in Android using Gson?
Give me some different examples. I can't understand the main purpose of using it.
Java class example,
public class Person {
#SerializedName("name")
private String personName;
#SerializedName("bd")
private String birthDate;
}
This class has two fields that represent the person name and birth date of a person. These fields are annotated with the #SerializedName annotation. The parameter (value) of this annotation is the name to be used when serialising and deserialising objects. For example, the Java field personName is represented as name in JSON.
JSON Example,
{
"name":"chintan",
"bd":"01-01-1990"
}
There are already few answers here,but I would like to add that if you are using ProGuard to Obfuscate your code & don't use #SerializedName("name") in your model class, then your GSON won't work. Because due to obfuscation, your variable names might have changed from String name to String a resulting into broken GSON parsing as GSON will look for key a into json & it will fail.
By specifying #SerializedName, GSON will not look in json based on variable name & will just use specified #SerializedName.
Of Course you can tell proguard to not obfuscate your model, but if you would like to have model obfuscated, then you must specify #SerializedName
Using #SerializedName you are actually telling the Parser when receiving a callback from the server i.e. of a Json format:
{
"name":"John Doe",
}
that when Serializing or Deserializing an object to instead of searching for a key named: "userName", in the Json response, to search for "name".
#SerializedName("name")
var userName: String,
This is good because you may have a model that you would like it to have its members being called with whatever you like.
You can instruct Proguard to not obfuscate your data classes by specifying #Keep on top of the class. This will neither remove nor obfuscate your class. No need to add #SerializedName to each and every field explicitly if the field name is similar to the Json key being used for it.
Let's say in a real-world scenario, your backend dev is giving you this response for an API request you make
{
"name":"John Doe",
"id":"1478"
}
Now, in the data class you make to handle this, there might be chances you want to specify a different variable name at Android side for the fields "name" and "id" that you are getting from backend.
#SerializedName comes to rescue here.
You just need to specify the actual key value you will be getting from backend in the #SerializedName (which will be used to serialize and deserialize) and then you can use a variable name of your choice that stores that value received from the operation.
For example, for the JSON I mentioned earlier, here is how its data class will look like:
data class User(
#SerializedName("name") val userName: String,
#SerializedName("id") val userId: Int
)
Here name, id is used in #SerializedName because it's the backend key.
But I have used userName, userId to store those values.

What is the basic purpose of #SerializedName annotation in Android using Gson

What is the basic purpose of #SerializedName annotation in Android using Gson?
Give me some different examples. I can't understand the main purpose of using it.
Java class example,
public class Person {
#SerializedName("name")
private String personName;
#SerializedName("bd")
private String birthDate;
}
This class has two fields that represent the person name and birth date of a person. These fields are annotated with the #SerializedName annotation. The parameter (value) of this annotation is the name to be used when serialising and deserialising objects. For example, the Java field personName is represented as name in JSON.
JSON Example,
{
"name":"chintan",
"bd":"01-01-1990"
}
There are already few answers here,but I would like to add that if you are using ProGuard to Obfuscate your code & don't use #SerializedName("name") in your model class, then your GSON won't work. Because due to obfuscation, your variable names might have changed from String name to String a resulting into broken GSON parsing as GSON will look for key a into json & it will fail.
By specifying #SerializedName, GSON will not look in json based on variable name & will just use specified #SerializedName.
Of Course you can tell proguard to not obfuscate your model, but if you would like to have model obfuscated, then you must specify #SerializedName
Using #SerializedName you are actually telling the Parser when receiving a callback from the server i.e. of a Json format:
{
"name":"John Doe",
}
that when Serializing or Deserializing an object to instead of searching for a key named: "userName", in the Json response, to search for "name".
#SerializedName("name")
var userName: String,
This is good because you may have a model that you would like it to have its members being called with whatever you like.
You can instruct Proguard to not obfuscate your data classes by specifying #Keep on top of the class. This will neither remove nor obfuscate your class. No need to add #SerializedName to each and every field explicitly if the field name is similar to the Json key being used for it.
Let's say in a real-world scenario, your backend dev is giving you this response for an API request you make
{
"name":"John Doe",
"id":"1478"
}
Now, in the data class you make to handle this, there might be chances you want to specify a different variable name at Android side for the fields "name" and "id" that you are getting from backend.
#SerializedName comes to rescue here.
You just need to specify the actual key value you will be getting from backend in the #SerializedName (which will be used to serialize and deserialize) and then you can use a variable name of your choice that stores that value received from the operation.
For example, for the JSON I mentioned earlier, here is how its data class will look like:
data class User(
#SerializedName("name") val userName: String,
#SerializedName("id") val userId: Int
)
Here name, id is used in #SerializedName because it's the backend key.
But I have used userName, userId to store those values.

Categories

Resources