Обработка неизвестных свойств Json при отображении объекта Json в POJO - PullRequest
0 голосов
/ 05 июня 2018

У меня 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, где любые неизвестные свойства в Json добавляются в поле «customProperties» .(NB! У меня также может быть «customProperties» в качестве карты, но имейте в виду, что объекты неизвестны и могут быть сложными).

Объект MyClass будет выглядеть следующим образом:

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

Я думал, что мне нужен специальный десериализатор, но то, что я пробовал, не отвечает моим потребностям, или моё понимание / компетенция могут отсутствовать.

Я могу использовать google.gson или org.json, не Джексон.

Ответы [ 2 ]

0 голосов
/ 05 июня 2018

Для этого требуется специальный адаптер типа постобработки, который может выполнять как десериализацию, так и сбор неизвестных свойств.Я сделал бы это следующим образом.

Следующий интерфейс является своего рода мостом между реальным объектом и адаптером типа.Здесь нет ничего особенного.

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}}
0 голосов
/ 05 июня 2018

Вы можете использовать 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));
...