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

com.launchdarkly.client.JsonHelpers Maven / Gradle / Ivy

package com.launchdarkly.client;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.launchdarkly.client.interfaces.SerializationException;

import java.io.IOException;

import static com.launchdarkly.client.VersionedDataKind.FEATURES;
import static com.launchdarkly.client.VersionedDataKind.SEGMENTS;

abstract class JsonHelpers {
  private static final Gson gson = new Gson();
  
  /**
   * Returns a shared instance of Gson with default configuration. This should not be used for serializing
   * event data, since it does not have any of the configurable behavior related to private attributes.
   * Code in _unit tests_ should _not_ use this method, because the tests can be run from other projects
   * in an environment where the classpath contains a shaded copy of Gson instead of regular Gson.
   */
  static Gson gsonInstance() {
    return gson;
  }
  
  /**
   * Creates a Gson instance that will correctly serialize users for the given configuration (private attributes, etc.).
   */
  static Gson gsonInstanceForEventsSerialization(EventsConfiguration config) {
    return new GsonBuilder()
        .registerTypeAdapter(LDUser.class, new LDUser.UserAdapterWithPrivateAttributeBehavior(config))
        .create();    
  }

  /**
   * Deserializes an object from JSON. We should use this helper method instead of directly calling
   * gson.fromJson() to minimize reliance on details of the framework we're using, and to ensure that we
   * consistently use our wrapper exception.
   * 
   * @param json the serialized JSON string
   * @param objectClass class of object to create
   * @return the deserialized object
   * @throws SerializationException if Gson throws an exception
   */
  static  T deserialize(String json, Class objectClass) throws SerializationException {
    try {
      return gson.fromJson(json, objectClass);
    } catch (Exception e) {
      throw new SerializationException(e);
    }
  }
  
  /**
   * Serializes an object to JSON. We should use this helper method instead of directly calling
   * gson.toJson() to minimize reliance on details of the framework we're using (except when we need to use
   * gsonInstanceForEventsSerialization, since our event serialization logic isn't well suited to using a
   * simple abstraction).
   * 
   * @param o the object to serialize
   * @return the serialized JSON string
   */
  static String serialize(Object o) {
    return gson.toJson(o);
  }
  
  /**
   * Deserializes a data model object from JSON that was already parsed by Gson.
   * 

* For built-in data model classes, our usual abstraction for deserializing from a string is inefficient in * this case, because Gson has already parsed the original JSON and then we would have to convert the * JsonElement back into a string and parse it again. So it's best to call Gson directly instead of going * through our abstraction in that case, but it's also best to implement that special-casing just once here * instead of scattered throughout the SDK. * * @param kind the data kind * @param parsedJson the parsed JSON * @return the deserialized item */ static VersionedData deserializeFromParsedJson(VersionedDataKind kind, JsonElement parsedJson) throws SerializationException { VersionedData item; try { if (kind == FEATURES) { item = gson.fromJson(parsedJson, FeatureFlag.class); } else if (kind == SEGMENTS) { item = gson.fromJson(parsedJson, Segment.class); } else { // This shouldn't happen since we only use this method internally with our predefined data kinds throw new IllegalArgumentException("unknown data kind"); } } catch (JsonParseException e) { throw new SerializationException(e); } return item; } /** * Implement this interface on any internal class that needs to do some kind of post-processing after * being unmarshaled from JSON. You must also add the annotation {@code JsonAdapter(JsonHelpers.PostProcessingDeserializableTypeAdapterFactory)} * to the class for this to work. */ static interface PostProcessingDeserializable { void afterDeserialized(); } static class PostProcessingDeserializableTypeAdapterFactory implements TypeAdapterFactory { @Override public TypeAdapter create(Gson gson, TypeToken type) { return new PostProcessingDeserializableTypeAdapter<>(gson.getDelegateAdapter(this, type)); } } private static class PostProcessingDeserializableTypeAdapter extends TypeAdapter { private final TypeAdapter baseAdapter; PostProcessingDeserializableTypeAdapter(TypeAdapter baseAdapter) { this.baseAdapter = baseAdapter; } @Override public void write(JsonWriter out, T value) throws IOException { baseAdapter.write(out, value); } @Override public T read(JsonReader in) throws IOException { T instance = baseAdapter.read(in); if (instance instanceof PostProcessingDeserializable) { ((PostProcessingDeserializable)instance).afterDeserialized(); } return instance; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy