从Java中的嵌套空对象创建示例JSON

时间:2018-05-24 18:01:26

标签: java json serialization jackson gson

我试图创建一个实用程序来从任何给定的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
}

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

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

这些是我尝试打印这些pojos的方法以及它们的输出并不能满足我的需求

杰克逊:

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

输出:

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

GSON:

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

输出:

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

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

2 个答案:

答案 0 :(得分:1)

我看不出这是怎么可能的。如果您有一个Long作为字段怎么办?杰克逊不知道该怎么办。杰克逊和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" : ""
    } ]
  } ]
}

希望这有帮助。

答案 1 :(得分:1)

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

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

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类型系统。

下一个类负责按类型模拟对象。 这有点困难,但评论会有所帮助(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": ""
}