微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

gson flat json到嵌套对象需要序列化器/解串器吗?

我有一些 JSON进来(我没有任何控制或能力来改变JSON中的结构和/或命名…这个问题中要记住这一点很重要),它具有类似于此的“扁平”结构:
{
  "name": "...","email": "...","Box_background_color": "...","Box_border_color": "...","Box_text_color": "...",...
}

现在,我可以创建一个简单的对象来保持一切平坦,如下所示:

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;

  ...
}

但是,我希望与盒子设置相关的所有内容都在它自己的类中(BoxSettings).这更像我想要的:

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;

  ...
}

我知道如果JSON的结构使得盒子设置是嵌套的,那么很容易实现我想要的,但是,我没有能力改变JSON的结构,所以请不要建议(如果可以,我会这样做).

我的问题是:创建一个完整的TypeAdapter是实现我想要的唯一方法,还是我仍然可以通过注释完成大部分工作?如果不是唯一的方法,我怎么能在不改变JSON的情况下完成这个呢?

以下是“创建整个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;
  }
}

解决方法

TypeAdapter不是唯一的方法,但在这种情况下,这是最好的方法,因为您可以将适配器与Gson实例(或您正在使用的任何库)相关联,并在那里拥有所有映射代码.

另一种方法是使用JAVA反射.我以前在我的项目中使用了以下代码的版本,但从未使用过JSON,从不使用嵌套对象(大多数时候没有其他选择,或者我想将sql结果集映射到Java对象而不调用resultSet.get … 很多时间).

这将适用于这种情况.

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,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's 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 + "]";
        }
    }

产量

{"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]]

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐