【问题标题】:Create example JSON from nested empty objects in Java从 Java 中的嵌套空对象创建示例 JSON
【发布时间】:2018-11-04 01:05:45
【问题描述】:

我正在尝试创建一个实用程序来打印来自任何给定 POJO 的示例 JSON 结构。我一直在尝试 Jackson 和 Gson 打印给定对象的所有字段。我创建了以下三个对象作为示例。

public class Model {
    private String val1;
    private Child child;    
//getters and setters
}

public class Child {
    private String val2;
    private ArrayList<SubChild> subChildren;
//getters and setters
}

public class SubChild {
    private String val3;
//getters and setters
}

我想要一个示例序列化程序,即使子项为空,也可以打印这些对象及其所有字段名称。以下是我的目标输出:

{
  "val1" : "",
  "child" : {
    "val2" : "",
    "subChildren" : [ {
      "val3" : ""
    } ]
  }
}

这些是我尝试打印这些 pojo 及其输出的方法,但与我的需求不太匹配

杰克逊:

ObjectMapper map = new ObjectMapper().setSerializationInclusion(Include.ALWAYS);
Model testModel = new Model()
map.writerWithDefaultPrettyPrinter().writeValueAsString(testModel);

输出:

{
  "val1" : null,
  "child" : null
}

格森:

Gson gson = builder.serializeNulls().setPrettyPrinting().create();
Model testModel = new Model();
gson.toJson(testModel);

输出:

{
  "val1" : null,
  "child" : null
}

有没有一种简单的方法可以在不填充所有子字段的情况下实现我的目标?我希望能够在我不知道要调用哪些方法来用空值填充对象的泛型类上使用此实用程序。

【问题讨论】:

    标签: java json serialization jackson gson


    【解决方案1】:

    我看不出这怎么可能。如果你有一个Long 作为一个字段呢?杰克逊不知道在那里做什么。 Jackson 和 Gson 所做的是正确的,他们打印了 null

    可以做的是编写一个实用程序来手动设置字段。但是,您必须相应地处理不同的类型。像这样的东西可以实现您的要求,但仅适用于List

    public static void main(String args[]) throws IOException, IllegalAccessException {
            ObjectMapper map = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.ALWAYS);
            Model testModel = new Model();
    
            instantiateFields(testModel);
    
            String result = map.writerWithDefaultPrettyPrinter().writeValueAsString(testModel);
            System.out.println(result);
        }
    
        private static void instantiateFields(Object o) throws IllegalAccessException {
            Field[] fields = o.getClass().getDeclaredFields();
    
            for (Field field : fields) {
                field.setAccessible(true);
    
                if (field.get(o) == null) {
                    Type type = field.getType();
    
                    try {
                        Class<?> clazz = (Class<?>) type;
                        Object instance = clazz.newInstance();
    
                        if (List.class.isAssignableFrom(clazz)) {
                            instantiateList(clazz, field, instance);
                        }
    
                        field.set(o, instance);
                        instantiateFields(instance);
    
                    } catch (ClassCastException | InstantiationException e) {
                        // Handle this or leave field null
                    }
                }
            }
        }
    
        private static void instantiateList(Class<?> clazz, Field field, Object instance) throws IllegalAccessException, InstantiationException {
            ParameterizedType listType = (ParameterizedType) field.getGenericType();
            Class<?> listClass = (Class<?>) listType.getActualTypeArguments()[0];
    
            Object listTypeInstance = listClass.newInstance();
    
            instantiateFields(listTypeInstance);
    
            List<Object> list = (List<Object>) instance;
            list.add(listTypeInstance);
        }
    

    产生以下输出:

    {
      "val1" : "",
      "child" : [ {
        "val2" : "",
        "subChildren" : [ {
          "val3" : ""
        } ]
      } ]
    }
    

    希望这会有所帮助。

    【讨论】:

    • 这很有帮助!我所需要修改的只是拥有Class&lt;?&gt; clazz = (Class&lt;?&gt;) type; if(clazz == List.class){ clazz = ArrayList.class; } Object instance = clazz.newInstance(); 的try 块,它就像一个魅力!尝试实例化 List.class 抛出错误
    • 很高兴能帮上忙!如果你不介意接受答案。您也可以很容易地添加Map 和其他类型!
    【解决方案2】:

    我怀疑任何库都支持这种开箱即用的功能,因为它们唯一的职责是处理序列化/反序列化。 因此,为了使 任何 序列化程序按您的意愿工作,最好创建一个对象模拟,然后尝试为特定库调整模拟策略。

    首先,让我们为类型创建一个简单的实用程序类:

    final class Types {
    
        private Types() {
        }
    
        static <T> Class<T> typeToClass(final Type type) {
            final Class<?> clazz;
            if ( type instanceof Class ) {
                clazz = (Class<?>) type;
            } else if ( type instanceof ParameterizedType ) {
                final ParameterizedType parameterizedType = (ParameterizedType) type;
                clazz = typeToClass(parameterizedType.getRawType());
            } else {
                throw new AssertionError(type);
            }
            @SuppressWarnings("unchecked")
            final Class<T> castClass = (Class<T>) clazz;
            return castClass;
        }
    
    }
    

    上面的方法只负责从一个类型中解析一个类(至少它试图这样做)。 类是类型,但类型不一定是类。 嗯,这就是他们的 Java 类型系统。

    下一个类负责按类型模拟对象。 这有点困难,但 cmets 会有所启发(ImmutableMapImmutableList 来自 Google Guava)。

    final class Mock {
    
        // Cache immutable primitives and wrappers. It's safe   
        private static final Optional<Byte> defaultByte = Optional.of((byte) 0);
        private static final Optional<Short> defaultShort = Optional.of((short) 0);
        private static final Optional<Integer> defaultInteger = Optional.of(0);
        private static final Optional<Long> defaultLong = Optional.of(0L);
        private static final Optional<Float> defaultFloat = Optional.of(0F);
        private static final Optional<Double> defaultDouble = Optional.of(0D);
        private static final Optional<Character> defaultCharacter = Optional.of('\u0000');
        private static final Optional<Boolean> defaultBoolean = Optional.of(false);
        private static final Optional<String> defaultString = Optional.of("");
    
        // This is a simple map that can return a value by a known type
        private static final Map<Class<?>, Optional<?>> defaultObjectsByIndex = ImmutableMap.<Class<?>, Optional<?>>builder()
                .put(byte.class, defaultByte).put(Byte.class, defaultByte)
                .put(short.class, defaultShort).put(Short.class, defaultShort)
                .put(int.class, defaultInteger).put(Integer.class, defaultInteger)
                .put(long.class, defaultLong).put(Long.class, defaultLong)
                .put(float.class, defaultFloat).put(Float.class, defaultFloat)
                .put(double.class, defaultDouble).put(Double.class, defaultDouble)
                .put(char.class, defaultCharacter).put(Character.class, defaultCharacter)
                .put(boolean.class, defaultBoolean).put(Boolean.class, defaultBoolean)
                .put(String.class, defaultString)
                .build();
    
        // Unlike the previous map, searching for an object takes a linear type
        // The first-best candidate will be used
        // The ifAssignable method is declared below
        private static final Collection<? extends Function<? super Type, Optional<?>>> defaultObjectsByFirstBest = ImmutableList.<Function<? super Type, Optional<?>>>builder()
                .add(ifAssignable(LinkedList.class, LinkedList::new))
                .add(ifAssignable(ArrayList.class, ArrayList::new))
                .add(ifAssignable(List.class, ArrayList::new))
                .add(ifAssignable(TreeSet.class, TreeSet::new))
                .add(ifAssignable(LinkedHashSet.class, LinkedHashSet::new))
                .add(ifAssignable(HashSet.class, HashSet::new))
                .add(ifAssignable(Set.class, HashSet::new))
                .add(ifAssignable(TreeMap.class, TreeMap::new))
                .add(ifAssignable(LinkedHashMap.class, LinkedHashMap::new))
                .add(ifAssignable(HashMap.class, HashMap::new))
                .add(ifAssignable(Map.class, HashMap::new))
                .build();
    
        private Mock() {
        }
    
        static <T> T create(
                final Type type,
                final Predicate<? super Type> isTypeSupported,
                final Predicate<? super Field> isFieldSupported,
                final Function<? super Class<T>, ? extends T> objectCreator,
                final BiFunction<Object, Type, Object> postProcess
        )
                throws Exception {
            return create(type, Mock::supplyDefaultInstance, isTypeSupported, isFieldSupported, objectCreator, postProcess);
        }
    
        static <T> T create(
                final Type type, // Used to instantiate an object 
                final Function<? super Type, Optional<?>> defaultInstanceSupplier, // Used in order to supply a default object by type
                final Predicate<? super Type> isTypeSupported, // Not all types can be serialized
                final Predicate<? super Field> isFieldSupported, // Not all fields can be serialized
                final Function<? super Class<T>, ? extends T> objectCreator, // If no any default object can be supplied, try to ask for it from elsewhere
                final BiFunction<Object, Type, Object> postProcess // This is what is what used to post-process the result object
        )
                throws Exception {
            // Not something we want to support?
            if ( !isTypeSupported.test(type) ) {
                return null;
            }
            // Check if we can provide a default value 
            final Optional<?> maybeT = defaultInstanceSupplier.apply(type);
            if ( maybeT.isPresent() ) {
                @SuppressWarnings("unchecked")
                final T castT = (T) postProcess.apply(maybeT.get(), type);
                return castT;
            }
            final Class<T> clazz = Types.typeToClass(type);
            // No? Then let's try instantiate it
            final T newT = objectCreator.apply(clazz);
            // And iterate it from bottom to top to super classes (java.lang.Object does not have fields)
            for ( Class<?> i = clazz; i != null && i != Object.class; i = i.getSuperclass() ) {
                for ( final Field field : i.getDeclaredFields() ) {
                    if ( isFieldSupported.test(field) ) {
                        field.setAccessible(true);
                        // Recursively do the same for all the fields
                        final Object value = create(field.getGenericType(), defaultInstanceSupplier, isTypeSupported, isFieldSupported, objectCreator, postProcess);
                        field.set(newT, value);
                    }
                }
            }
            // And then do post-processing for these "special" types 
            @SuppressWarnings("unchecked")
            final T castNewT = (T) postProcess.apply(newT, type);
            return castNewT;
        }
    
        static Optional<?> supplyDefaultInstance(final Type type) {
            final Optional<?> defaultValueFromIndex = defaultObjectsByIndex.get(type);
            if ( defaultValueFromIndex != null ) {
                return defaultValueFromIndex;
            }
            return defaultObjectsByFirstBest
                    .stream()
                    .map(resolver -> resolver.apply(type))
                    .filter(Optional::isPresent)
                    .findFirst()
                    .orElse(Optional.empty());
        }
    
        private static Function<? super Type, Optional<?>> ifAssignable(final Class<?> expectedClass, final Supplier<?> defaultObject) {
            return actualClass -> expectedClass.isAssignableFrom(Types.typeToClass(actualClass))
                    ? Optional.of(defaultObject.get())
                    : Optional.empty();
        }
    
    }
    

    一旦你拥有了这一切,你就可以拥有一个 Gson 特化并且进行后处理,比如将模拟元素添加到集合中(集合默认是并且应该是空的)。

    public final class Q50515517 {
    
        private Q50515517() {
        }
    
        private static final Gson gson = new GsonBuilder()
                .serializeNulls()
                .setPrettyPrinting()
                .disableHtmlEscaping()
                .create();
    
        // Gson can avoid use of constructors
        private static final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
    
        public static void main(final String... args)
                throws Exception {
            gson.toJson(create(Object3.class, gson, Q50515517::customPostProcess), Object3.class, System.out);
        }
    
        // Here we make some Gson adaptations
        private static <T> T create(final Type type, final Gson gson, final BiFunction<Object, Type, Object> postProcess)
                throws Exception {
            final Excluder excluder = gson.excluder();
            final Predicate<? super Type> isClassSupported = t -> !excluder.excludeClass(Types.typeToClass(t), true);
            final Predicate<? super Field> isFieldSupported = field -> !excluder.excludeField(field, true);
            return Mock.create(type, Mock::supplyDefaultInstance, isClassSupported, isFieldSupported, Q50515517::createUnsafely, postProcess);
        }
    
        private static <T> T createUnsafely(final Class<T> clazz) {
            try {
                return unsafeAllocator.newInstance(clazz);
            } catch ( final Exception ex ) {
                throw new RuntimeException(ex);
            }
        }
    
        private static Object customPostProcess(final Object object, final Type type) {
            if ( object instanceof Collection ) {
                if ( type instanceof ParameterizedType ) {
                    @SuppressWarnings("unchecked")
                    final Collection<Object> collection = (Collection<Object>) object;
                    final ParameterizedType parameterizedType = (ParameterizedType) type;
                    final Type elementType = parameterizedType.getActualTypeArguments()[0];
                    try {
                        final Object newElement = create(elementType, gson, Q50515517::customPostProcess);
                        collection.add(newElement); // This is where we add a mock element to the collection
                        return object;
                    } catch ( final Exception ex ) {
                        throw new RuntimeException(ex);
                    }
                }
            }
            return object;
        }
    
    }
    

    综上所述,该解决方案支持:

    • 没有默认构造函数的类
    • 对象类层次结构字段
    • 排除策略
    • 接口

    上面测试中的示例类:

    class Object1 {
        String s1;
    }
    
    class Object2
            extends Object1 {
        String s2;
    }
    
    class Object3
            extends Object2 {
        String s3;
        List<Object4> lo4;
    }
    
    class Object4 {
        String s4a;
        String s4b;
    }
    

    输出:

    {
      "s3": "",
      "lo4": [
        {
          "s4a": "",
          "s4b": ""
        }
      ],
      "s2": "",
      "s1": ""
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-02
      • 1970-01-01
      • 1970-01-01
      • 2018-12-29
      • 2017-02-14
      • 2017-12-22
      • 2014-05-15
      • 1970-01-01
      相关资源
      最近更新 更多