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

com.aevi.util.json.JsonConverter Maven / Gradle / Ivy

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.aevi.util.json;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Base64;

import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

/**
 * This class contains a number of useful serialiser/deserialiser implementations to handle the objects in this library.
 *
 * It also contains a serialiser/desrialiser to allow Android {@link Bitmap} objects to be stored in JSON.
 */
public final class JsonConverter {

    private static final Gson GSON;
    private static final Gson GSON_WITH_METHODS;

    static {
        GSON = new GsonBuilder()
                .registerTypeAdapter(JsonOption.class, new ExtrasDeserialiser())
                .registerTypeAdapter(Bitmap.class, new BitmapDeserialiser())
                .registerTypeAdapter(Bitmap.class, new BitmapSerialiser())
                .registerTypeAdapterFactory(new PostProcessingHandler())
                .create();
        GSON_WITH_METHODS = new GsonBuilder()
                .registerTypeAdapter(JsonOption.class, new ExtrasDeserialiser())
                .registerTypeAdapter(Bitmap.class, new BitmapDeserialiser())
                .registerTypeAdapter(Bitmap.class, new BitmapSerialiser())
                .registerTypeAdapterFactory(new ExposedMethodProcessingHandler())
                .create();
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD) //can use in method only.
    public @interface ExposeMethod {
        /**
         * @return The name of the field to store the serialized result of the method
         */
        String value();
    }

    public static String serialize(Object object) {
        return GSON.toJson(object);
    }

    public static String serializeWithExposedMethods(Object object) {
        return GSON_WITH_METHODS.toJson(object);
    }

    public static JsonElement serializeToTree(Object object) {
        return GSON.toJsonTree(object);
    }

    public static JsonElement serializeToTreeWithExposedMethods(Object object) {
        return GSON_WITH_METHODS.toJsonTree(object);
    }

    public static  T deserialize(String json, Class type) throws JsonParseException {
        return type.cast(GSON.fromJson(json, type));
    }

    private static class ExposedMethodProcessingHandler implements TypeAdapterFactory {
        @Override
        public  TypeAdapter create(Gson gson, TypeToken type) {
            final TypeAdapter delegate = gson.getDelegateAdapter(this, type);
            return new TypeAdapter() {
                @Override
                public void write(JsonWriter out, T src) throws IOException {
                    JsonElement element = delegate.toJsonTree(src);

                    if(element.isJsonObject()) {
                        JsonObject object = (JsonObject)element;
                        for (Method m : getAnnotatedMembers(src.getClass())) {
                            try {
                                m.setAccessible(true);
                                Object value = m.invoke(src);
                                object.add(m.getAnnotation(com.aevi.util.json.JsonConverter.ExposeMethod.class).value(), GSON_WITH_METHODS.toJsonTree(value));
                            } catch (InvocationTargetException | IllegalAccessException e) {
                                // fall thru...
                            }
                        }
                        GSON_WITH_METHODS.toJson(object, out);
                    } else {
                        delegate.write(out, src);
                    }
                }

                @Override
                public T read(JsonReader in) throws IOException {
                    return delegate.read(in);
                }

                private Method[] getAnnotatedMembers(Class jsonable) {
                    Method[] methods = jsonable.getMethods();
                    List annotated = new ArrayList<>();
                    for (Method m : methods) {
                        if (m.getAnnotation(com.aevi.util.json.JsonConverter.ExposeMethod.class) != null) {
                            annotated.add(m);
                        }
                    }
                    return annotated.toArray(new Method[annotated.size()]);
                }
            };
        }
    }

    private static class PostProcessingHandler implements TypeAdapterFactory {

        @Override
        public  TypeAdapter create(Gson gson, TypeToken type) {
            final TypeAdapter delegate = gson.getDelegateAdapter(this, type);
            return new TypeAdapter() {
                @Override
                public void write(JsonWriter out, T src) throws IOException {
                    delegate.write(out, src);
                }

                @Override
                public T read(JsonReader in) throws IOException {
                    T object = delegate.read(in);
                    if (object instanceof JsonPostProcessing) {
                        ((JsonPostProcessing) object).onJsonDeserialisationCompleted();
                    }
                    return object;
                }
            };
        }
    }

    private static class BitmapSerialiser implements JsonSerializer {

        @Override
        public JsonElement serialize(Bitmap src, Type typeOfSrc, JsonSerializationContext context) {
            return new JsonPrimitive(writeBitmap(src));
        }
    }

    private static class BitmapDeserialiser implements JsonDeserializer {

        @Override
        public Bitmap deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            return readBitmap(json.getAsString());
        }
    }

    private static class ExtrasDeserialiser implements JsonDeserializer {

        public JsonOption deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
            JsonObject obj = json.getAsJsonObject();
            JsonElement entry = obj.get("value");
            String className = obj.get("type").getAsString();
            try {
                Class clazz = Class.forName(className);
                return new JsonOption(context.deserialize(entry, clazz));
            } catch (ClassNotFoundException e) {
                return new JsonOption(entry, className);
            }
        }
    }

    private static String writeBitmap(Bitmap bitmap) {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.WEBP, 70, stream);
        return Base64.encodeToString(stream.toByteArray(), Base64.DEFAULT);
    }

    private static Bitmap readBitmap(String in) {
        byte[] pngBytes = Base64.decode(in.getBytes(), Base64.DEFAULT);
        return BitmapFactory.decodeByteArray(pngBytes, 0, pngBytes.length);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy