【问题标题】:Serialize list of objects, keyed by attribute序列化对象列表,以属性为键
【发布时间】:2020-09-01 15:58:31
【问题描述】:

假设我有一堂课:

public class Person {
   String name;
   Int age;
}

以及该类的对象列表:

List<Person> people = ...

通常,通过 JacksonGson 等序列化程序运行此程序会导致:

"[{'name':'John','age':42},{'name':'Sam','age':43}]

但我希望序列化为单个 json 对象,其中每个属性都是包含属性的列表,如下所示:

"{'name':['John','Sam'],'age':[42,43]}"

任何序列化库都支持这个吗?

【问题讨论】:

    标签: java json serialization jackson gson


    【解决方案1】:

    我会创建一种“包装器”,它可以接收任意数量的人员,并以一种让他们以这种方式序列化的方式存储字段。因此,在这种情况下,您将创建一系列人员,创建一个包含这些人员的包装器,然后对其进行序列化。

    public class PersonWrapper {
    
        private int[] ages;
        private String[] names;
    
        public PersonWrapper(Person... persons) {
            ages = new int[persons.length];
            names = new String[persons.length];
    
            for (int i = 0; i < persons.length; i++) {
                ages[i] = persons[i].getAge();
                names[i] = persons[i].getName();
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      将您的 List&lt;Person&gt; 转换为新对象。

      class NewClass {
          List<String> name;
          List<Integer> ages;
      }
      

      然后将这个对象通过Serializer传递得到: "{'name':['John','Sam'],'age':[42,43]}"

      【讨论】:

        【解决方案3】:

        序列化库通常不是为这样的东西而设计的。 您正在寻找的是 JSON 树转换,您可以在 Gson 和 Jackson 中轻松实现它。

        这是一个 Gson 的转换示例:

        final class Transformations {
        
            private Transformations() {
            }
        
            static JsonObject transposeShallow(final Iterable<? extends JsonElement> inArray) {
                final JsonObject outObject = new JsonObject();
                for ( final String name : scanAllNames(inArray) ) {
                    final JsonArray outSubArray = new JsonArray();
                    for ( final JsonElement inJsonElement : inArray ) {
                        if ( !inJsonElement.isJsonObject() ) {
                            throw new IllegalArgumentException(inJsonElement + " is not a JSON object");
                        }
                        outSubArray.add(inJsonElement.getAsJsonObject().get(name));
                    }
                    outObject.add(name, outSubArray);
                }
                return outObject;
            }
        
            private static Iterable<String> scanAllNames(final Iterable<? extends JsonElement> jsonArray) {
                final ImmutableSet.Builder<String> allNames = new ImmutableSet.Builder<>();
                for ( final JsonElement jsonElement : jsonArray ) {
                    if ( !jsonElement.isJsonObject() ) {
                        throw new IllegalArgumentException(jsonElement + " is not a JSON object");
                    }
                    allNames.addAll(jsonElement.getAsJsonObject().keySet());
                }
                return allNames.build();
            }
        
        }
        

        上述转换可以合并到序列化过程中,但这会影响对象的结构(例如,如何指示根对象及其字段的可转置集合?如何引入新的“转置”类型并将其合并到您的数据模型类型系统?必要时如何正确反序列化?)。

        获得 JSON 树后,您可以将其转置到任何嵌套级别。 只要您不需要转换嵌套元素,转置根元素是最简单的方法。

        private static final Type peopleType = new TypeToken<Collection<Person>>() {}.getType();
        
        public static void main(final String... args) {
            System.out.println(gson.toJson(people, peopleType));
            System.out.println(gson.toJson(Transformations.transposeShallow(gson.toJsonTree(people, peopleType).getAsJsonArray())));
        }
        

        给出:

        [{"name":"John","age":42},{"name":"Sam","age":43}]
        {"name":["John","Sam"],"age":[42,43]}
        

        上面的解决方案几乎可以处理您代码中的任何类型,但是对于构建 JSON 树有一点点损失。 Schred 建议的 PersonWrapper 之类的东西可能是另一种选择,但这需要您所有类型的包装器,并且一旦您的包装类发生更改,它们就需要更新。

        此外,您可能还对像 Jolt 这样专为 JSON 转换而设计的库感兴趣(但不确定在 Jolt 中是否可行)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-07-09
          • 2018-04-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-28
          相关资源
          最近更新 更多