All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.slack.api.util.json.UnknownPropertyDetectionAdapterFactory Maven / Gradle / Ivy

There is a newer version: 1.45.3
Show newest version
package com.slack.api.util.json;

import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * https://github.com/google/gson/issues/188#issuecomment-282746095
 */
public class UnknownPropertyDetectionAdapterFactory implements TypeAdapterFactory {

    @Override
    public  TypeAdapter create(Gson gson, TypeToken type) {
        // If the type adapter is a reflective type adapter, we want to modify the implementation using reflection. The
        // trick is to replace the Map object used to look up the property name. Instead of returning null if the
        // property is not found, we throw a Json exception to terminate the deserialization.
        TypeAdapter delegate = gson.getDelegateAdapter(this, type);

        // Check if the type adapter is a reflective, cause this solution only work for reflection.
        if (delegate instanceof ReflectiveTypeAdapterFactory.Adapter) {

            // This code is only compatible with GSON 2.11.0+
            try {
                Class adaptorClass = delegate.getClass();
                Field fieldsDataField = null;
                while (fieldsDataField == null && !adaptorClass.equals(Object.class)) {
                    try {
                        fieldsDataField = adaptorClass.getDeclaredField("fieldsData");
                    } catch (NoSuchFieldException _ignore) {
                        // Since GSON v2.10, the internal class hierarchy has been changed
                        // 1) com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$FieldReflectionAdapter
                        // 2) com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter
                        adaptorClass = adaptorClass.getSuperclass();
                    }
                }
                if (fieldsDataField == null) {
                    String message = "Failed to access fieldsData inside the GSON library";
                    throw new IllegalStateException(message);
                }
                fieldsDataField.setAccessible(true);
                Object fieldData = fieldsDataField.get(delegate);
                Field deserializedFieldsField = fieldData.getClass().getDeclaredField("deserializedFields");
                deserializedFieldsField.setAccessible(true);
                Map deserializedFields = (Map) deserializedFieldsField.get(fieldData);
                StringBuilder sb = new StringBuilder();
                for (String key : deserializedFields.keySet()) {
                    sb.append(key + ", ");
                }
                final String boundFieldsStr = sb.append("... (" + type.getType().getTypeName() + ")").toString();

                // Then replace it with our implementation throwing exception if the value is null.
                deserializedFields = new LinkedHashMap(deserializedFields) {

                    @Override
                    public Object get(Object key) {

                        Object value = super.get(key);
                        if (value == null) {
                            throw new JsonParseException("Unknown property detected: " + key + " in " + boundFieldsStr);
                        }
                        return value;

                    }

                };
                // Finally, push our custom map back using reflection.
                deserializedFieldsField.set(fieldData, deserializedFields);

            } catch (Exception e) {
                // Should never happen if the implementation doesn't change.
                throw new IllegalStateException(e);
            }

        }
        return delegate;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy