Gson flat json for nested objects require serializer / deserializer?

I have some kind of JSON (I have no control or ability to change the structure and / or names in JSON ... it is important to keep this in mind), which has a "flat" structure similar to this:

{ "name": "...", "email": "...", "box_background_color": "...", "box_border_color": "...", "box_text_color": "...", ... } 


Now I can just create a simple object that holds everything as I like:

 public class Settings { @SerializedName("name") private String _name; @SerializedName("email") private String _emailAddress; @SerializedName("box_background_color") private String _boxBackgroundColor; @SerializedName("box_border_color") private String _boxBorderColor; @SerializedName("box_text_color") private String _boxTextColor; ... } 


However, I want everything related to box parameters to be in its own class ( BoxSettings ). This is more like what I want:

 public class Settings { @SerializedName("name") private String _name; @SerializedName("email") private String _emailAddress; private BoxSettings _boxSettings ... } public class BoxSettings { @SerializedName("box_background_color") private String _boxBackgroundColor; @SerializedName("box_border_color") private String _boxBorderColor; @SerializedName("box_text_color") private String _boxTextColor; ... } 


I know that if JSON was structured so that the mailbox settings were nested, then it would be easy to accomplish what I want, however, I have no way to change the JSON structure, so please t assume that (I would do this if I could).

My question is this: Does the entire TypeAdapter create the only way to accomplish what I want, or can I accomplish most of this with annotations? If this is not the only way, how else can I do this without changing the JSON?

The following is an example of what I mean by "creating an integer TypeAdapter":

 public class SettingsTypeAdapter implements JsonDeserializer<Settings>, JsonSerializer<Settings> { @Override public JsonElement serialize(Settings src, Type typeOfSrc, JsonSerializationContext context) { // Add _name // Add _emailAddress // Add BoxSettings._boxBackgroundColor // Add BoxSettings._boxBorderColor // Add BoxSettings._boxTextColor return jsonElement; } @Override public Settings deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { // Read _name // Read _emailAddress // Read BoxSettings._boxBackgroundColor // Read BoxSettings._boxBorderColor // Read BoxSettings._boxTextColor return settings; } } 
+6
source share
1 answer

The TypeAdapter adapter is not the only way, but in this case it is the best way, since you can associate the adapter with a Gson instance (or any other library that you use) and have all your display code.

Another way is to use JAVA reflection. I used the below code version in my projects before, but never with JSON and never with nested objects (basically, when there was no other choice, or if I wanted to map the SQL result set to a Java object without calling resultSet.get... many times).

This will work in this case.

 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.json.JSONObject; public class Main { public static void main(String[] args) { try { String json = "{\"name\": \"test name\", \"email\": \" email@email.com \", \"box_background_color\": \"red\", \"box_border_color\": \"orange\", \"box_text_color\": \"white\", \"test3_var2\":3}"; JSONObject jsonObject = new JSONObject(json); System.out.println(jsonObject); System.out.println(); /* * need to parse JSON into a map of String, Object */ Map<String, Object> mapAll = new HashMap<String, Object>(); Iterator<String> iter = jsonObject.keys(); while (iter.hasNext()) { String key = (String) iter.next(); Object value = jsonObject.get(key); mapAll.put(key, value); System.out.println(key + "::::" + value); } System.out.println(); /* * use the mapper to generate the objects */ MyMapper<TestClass1> myMapper = new MyMapper<TestClass1>(); TestClass1 result = myMapper.mapToObject(mapAll, TestClass1.class); System.out.println(result); } catch (Exception e) { e.printStackTrace(); } } } class MyMapper<T> { @SuppressWarnings("unchecked") public T mapToObject(Map<String, Object> flatStructure, Class<T> objectClass) { T result = null; Field[] fields = null; try { // new base object result = objectClass.newInstance(); // get all of its fields fields = objectClass.getDeclaredFields(); for (Field field : fields) { // normal variable if (field.isAnnotationPresent(MyColumn.class)) { String variableKey = field.getAnnotation(MyColumn.class).variableKey(); setJavaFieldValue(result, field.getName(), flatStructure.get(variableKey)); } // variable that is an object and itself has to be mapped else if (field.isAnnotationPresent(MyInnerColumn.class)) { String startsWith = field.getAnnotation(MyInnerColumn.class).startsWith(); // reduce the map to only have attributes that are related to this field Map<String, Object> reducedMap = reduceMap(startsWith, flatStructure); // make sure that there are attributes for the inner object if (reducedMap != null) { // map the inner object MyMapper<T> myMapper = new MyMapper<T>(); T t2 = myMapper.mapToObject(reducedMap, (Class<T>) field.getType()); // set the mapped object to the base objecct setJavaFieldValue(result, field.getName(), t2); } } else { // no annotation on the field so ignored } } } catch (Exception e) { e.printStackTrace(); } return result; } private Map<String, Object> reduceMap(String startsWith, Map<String, Object> mapToReduce) { Map<String, Object> result = new HashMap<String, Object>(); for (Map.Entry<String, Object> entry : mapToReduce.entrySet()) { if (entry.getKey().toLowerCase().startsWith(startsWith.toLowerCase())) { result.put(entry.getKey(), entry.getValue()); } } return result.size() == 0 ? null : result; } private void setJavaFieldValue(Object object, String fieldName, Object fieldValue) { try { Field field = object.getClass().getDeclaredField(fieldName); boolean fieldAccess = field.isAccessible(); // make the field accessible field.setAccessible(true); field.set(object, fieldValue); // put it back to the way it was field.setAccessible(fieldAccess); } catch (Exception e) { e.printStackTrace(); } } } /* * Annotation for a regular variable / field */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface MyColumn { // the variable JSON key String variableKey() default ""; } /* * Annotation for an inner / nested variable / field */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface MyInnerColumn { /* * JSON keys that start with this string will be * associated with this nested field */ String startsWith() default ""; } class TestClass1 { @MyColumn(variableKey = "name") private String _name; @MyColumn(variableKey = "email") private String _emailAddress; @MyInnerColumn(startsWith = "box_") private TestClass2 innerClass; @MyInnerColumn(startsWith = "test3_") private TestClass3 innerClass2; @Override public String toString() { return "TestClass1 [_name=" + _name + ", _emailAddress=" + _emailAddress + ", innerClass=" + innerClass + ", innerClass2=" + innerClass2 + "]"; } } class TestClass2 { @MyColumn(variableKey = "box_background_color") private String _boxBackgroundColor; @MyColumn(variableKey = "box_border_color") private String _boxBorderColor; @MyColumn(variableKey = "box_text_color") private String _boxTextColor; @Override public String toString() { return "TestClass2 [_boxBackgroundColor=" + _boxBackgroundColor + ", _boxBorderColor=" + _boxBorderColor + ", _boxTextColor=" + _boxTextColor + "]"; } } class TestClass3 { @MyColumn(variableKey = "test3_var1") private String _test3Var1; @MyColumn(variableKey = "test3_var2") private int _test3Var2; @Override public String toString() { return "TestClass3 [_test3Var1=" + _test3Var1 + ", _test3Var2=" + _test3Var2 + "]"; } } 

Output

 {"box_background_color":"red","box_text_color":"white","test3_var2":3,"name":"test name","email":" email@email.com ","box_border_color":"orange"} box_background_color::::red box_text_color::::white test3_var2::::3 name::::test name email:::: email@email.com box_border_color::::orange TestClass1 [_name=test name, _emailAddress=email@email.com , innerClass=TestClass2 [_boxBackgroundColor=red, _boxBorderColor=orange, _boxTextColor=white], innerClass2=TestClass3 [_test3Var1=null, _test3Var2=3]] 
0
source

Source: https://habr.com/ru/post/976533/


All Articles