有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java在将Json对象映射到POJO时处理未知Json属性

我的POJO看起来像这样:

public class MyClass {
    public String sub;
    public String iss;
    public JsonObject customProperties;

    ..constructor, getters, setters..
}

一个Json对象看起来像这样:

{
    "sub" : "value",
    "iss" : "value2",
    "unknown_property" : "value3",
    "unknown_property_2" : {
        "a" : 1,
        "b" : 2
    }
}

我想将Json对象映射到MyClass,在MyClass中,Json中的任何未知属性都被添加到“customProperties”字段中。(注意!我也可以使用“customProperties”作为地图,但请记住,对象是未知的,可能很复杂)

然后,MyClass对象将如下所示:

sub = "value"
iss = "value2"
customProperties = {"unknown_property":"value3","unknown_property_2":{"a":1,"b":2}}

我想我需要一个自定义反序列化程序,但我尝试过的东西不能满足我的需求,或者我可能缺乏理解/能力

我可以用谷歌。gson或org。json,而不是杰克逊


共 (2) 个答案

  1. # 1 楼答案

    这需要一个特殊的后处理类型适配器,它可以进行反序列化和收集未知属性。 我会像下面这样做

    下面的接口是真实对象和类型适配器之间的桥梁。 这里没什么特别的

    interface IUnknownPropertiesConsumer {
    
        void acceptUnknownProperties(JsonObject jsonObject);
    
    }
    

    现在,您可以使用以下映射来实现上述接口

    final class MyClass
            implements IUnknownPropertiesConsumer {
    
        final String sub = null;
        final String iss = null;
        transient JsonObject customProperties;
    
        @Override
        public void acceptUnknownProperties(final JsonObject customProperties) {
            this.customProperties = customProperties;
        }
    
    }
    

    请注意,如果出于任何原因无法更改映射,您仍然可以调整以下类型适配器来实现此目的:

    final class UnknownPropertiesTypeAdapterFactory
            implements TypeAdapterFactory {
    
        private static final TypeAdapterFactory instance = new UnknownPropertiesTypeAdapterFactory();
    
        private UnknownPropertiesTypeAdapterFactory() {
        }
    
        static TypeAdapterFactory get() {
            return instance;
        }
    
        @Override
        public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
            // Check if we can deal with the given type
            if ( !IUnknownPropertiesConsumer.class.isAssignableFrom(typeToken.getRawType()) ) {
                return null;
            }
            // If we can, we should get the backing class to fetch its fields from
            @SuppressWarnings("unchecked")
            final Class<IUnknownPropertiesConsumer> rawType = (Class<IUnknownPropertiesConsumer>) typeToken.getRawType();
            @SuppressWarnings("unchecked")
            final TypeAdapter<IUnknownPropertiesConsumer> delegateTypeAdapter = (TypeAdapter<IUnknownPropertiesConsumer>) gson.getDelegateAdapter(this, typeToken);
            // Excluder is necessary to check if the field can be processed
            // Basically it's not required, but it makes the check more complete 
            final Excluder excluder = gson.excluder();
            // This is crucial to map fields and JSON object properties since Gson supports name remapping
            final FieldNamingStrategy fieldNamingStrategy = gson.fieldNamingStrategy();
            final TypeAdapter<IUnknownPropertiesConsumer> unknownPropertiesTypeAdapter = UnknownPropertiesTypeAdapter.create(rawType, delegateTypeAdapter, excluder, fieldNamingStrategy);
            @SuppressWarnings("unchecked")
            final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) unknownPropertiesTypeAdapter;
            return castTypeAdapter;
        }
    
        private static final class UnknownPropertiesTypeAdapter<T extends IUnknownPropertiesConsumer>
                extends TypeAdapter<T> {
    
            private final TypeAdapter<T> typeAdapter;
            private final Collection<String> propertyNames;
    
            private UnknownPropertiesTypeAdapter(final TypeAdapter<T> typeAdapter, final Collection<String> propertyNames) {
                this.typeAdapter = typeAdapter;
                this.propertyNames = propertyNames;
            }
    
            private static <T extends IUnknownPropertiesConsumer> TypeAdapter<T> create(final Class<? super T> clazz, final TypeAdapter<T> typeAdapter,
                    final Excluder excluder, final FieldNamingStrategy fieldNamingStrategy) {
                final Collection<String> propertyNames = getPropertyNames(clazz, excluder, fieldNamingStrategy);
                return new UnknownPropertiesTypeAdapter<>(typeAdapter, propertyNames);
            }
    
            @Override
            public void write(final JsonWriter out, final T value)
                    throws IOException {
                typeAdapter.write(out, value);
            }
    
            @Override
            public T read(final JsonReader in) {
                // JsonParser holds no state so instantiation is a bit excessive, but Gson may change in the future
                final JsonParser jsonParser = new JsonParser();
                // In its simplest solution, we can just collect a JSON tree because its much easier to process
                final JsonObject jsonObjectToParse = jsonParser.parse(in).getAsJsonObject();
                final JsonObject unknownProperties = new JsonObject();
                for ( final Map.Entry<String, JsonElement> e : jsonObjectToParse.entrySet() ) {
                    final String propertyName = e.getKey();
                    // No in the object fields?
                    if ( !propertyNames.contains(propertyName) ) {
                        // Then we assume the property is unknown
                        unknownProperties.add(propertyName, e.getValue());
                    }
                }
                // First convert the above JSON tree to an object
                final T object = typeAdapter.fromJsonTree(jsonObjectToParse);
                // And do the post-processing
                object.acceptUnknownProperties(unknownProperties);
                return object;
            }
    
            private static Collection<String> getPropertyNames(final Class<?> clazz, final Excluder excluder, final FieldNamingStrategy fieldNamingStrategy) {
                final Collection<String> propertyNames = new ArrayList<>();
                // Class fields are declared per class so we have to traverse the whole hierarachy
                for ( Class<?> i = clazz; i.getSuperclass() != null && i != Object.class; i = i.getSuperclass() ) {
                    for ( final Field declaredField : i.getDeclaredFields() ) {
                        // If the class field is not excluded
                        if ( !excluder.excludeField(declaredField, false) ) {
                            // We can translate the field name to its property name counter-part
                            final String propertyName = fieldNamingStrategy.translateName(declaredField);
                            propertyNames.add(propertyName);
                        }
                    }
                }
                return propertyNames;
            }
    
        }
    
    }
    

    现在一次使用它:

    private static final Gson gson = new GsonBuilder()
            .registerTypeAdapterFactory(UnknownPropertiesTypeAdapterFactory.get())
            .create();
    
    ...
    
    // Assuming jsonReader is a reader to read your original JSON
    final MyClass o = gson.fromJson(jsonReader, MyClass.class);
    System.out.println(o.sub);
    System.out.println(o.iss);
    System.out.println(o.customProperties);
    

    然后输出如下:

    value
    value2
    {"unknown_property":"value3","unknown_property_2":{"a":1,"b":2}}
    
  2. # 2 楼答案

    您可以使用JSON java来实现这一点

    JSONObject jsonObject = new JSONObject(jsonString);
    MyClass myPojo =  new MyClass();
    
    myPojo.setSub(jsonObject.getString("sub"));
    myPojo.setIss(jsonObject.getString("iss"));
    myPojo.setCustomProperties(new JSONObject());
    
    String[] keys = JSONObject.getNames(jsonObject);
    
    for (String key : keys) 
        if(!(key.equals("sub") ||  key.equals("iss")))
            myPojo.getCustomProperties().put(key, jsonObject.get(key));