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

com.aliyun.odps.utils.GsonObjectBuilder Maven / Gradle / Ivy

The newest version!
package com.aliyun.odps.utils;

import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.aliyun.odps.Instance;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import com.google.gson.TypeAdapter;
import com.google.gson.internal.LinkedTreeMap;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;

/**
 * @author [email protected]
 *
 * A gson builder SINGLETON that can return a gson object with proper settings.
 * The gson object provided by this builder can deal with Object type
 * in a Map well, especially when encoutering Double/Long in fact but
 * declared as Object.
 */
public class GsonObjectBuilder {

  /**
   * An adapter which fixes Gson's drawback. It can deal with
   * Double/Long well.
   */
  public static class GsonObjectAdapter extends TypeAdapter {

    // Object TypeAdapter Delegate
    private final TypeAdapter delegate = new Gson().getAdapter(Object.class);

    @Override public Object read(JsonReader in) throws IOException {
      JsonToken token = in.peek();
      switch (token) {
        case BEGIN_ARRAY:
          List list = new ArrayList();
          in.beginArray();
          while (in.hasNext()) {
            list.add(read(in));
          }
          in.endArray();
          return list;

        case BEGIN_OBJECT:
          Map map = new LinkedTreeMap();
          in.beginObject();
          while (in.hasNext()) {
            map.put(in.nextName(), read(in));
          }
          in.endObject();
          return map;

        case STRING:
          return in.nextString();

        // two difference cases: Double & Long
        case NUMBER:
          String inStr = in.nextString();
          if (inStr.contains(".") || inStr.contains("e") || inStr.contains("E")) {
            return Double.parseDouble(inStr);
          } else {
            // for safety, check the overflow issue, return BigInteger when overflow occurs
            BigInteger num = new BigInteger(inStr);
            BigInteger longMax = new BigInteger(String.valueOf(Long.MAX_VALUE));
            BigInteger longMin = new BigInteger(String.valueOf(Long.MIN_VALUE));
            if (num.compareTo(longMax) > 0 || num.compareTo(longMin) < 0) {
              return num;
            }
          }
          return Long.parseLong(inStr);

        case BOOLEAN:
          return in.nextBoolean();

        case NULL:
          in.nextNull();
          return null;

        default:
          throw new IllegalStateException();
      }
    }

    @SuppressWarnings("unchecked")
    @Override public void write(JsonWriter out, Object value) throws IOException {
      if (value == null) {
        out.nullValue();
        return;
      }

      delegate.write(out, value);
    }
  }

  public static class TaskSummaryAdapter extends TypeAdapter {

    // Object TypeAdapter Delegate
    private final TypeAdapter delegate = new Gson().getAdapter(Object.class);

    @Override public Object read(JsonReader in) throws IOException {
      JsonToken token = in.peek();
      switch (token) {
        case BEGIN_ARRAY:
          List list = new ArrayList();
          in.beginArray();
          while (in.hasNext()) {
            list.add(read(in));
          }
          in.endArray();
          return list;

        case BEGIN_OBJECT:
          Instance.TaskSummary ts = new Instance.TaskSummary();
          in.beginObject();
          while (in.hasNext()) {
            ts.put(in.nextName(), read(in));
          }
          in.endObject();
          return ts;

        case STRING:
          return in.nextString();

        case NUMBER:
          String inStr = in.nextString();
          if (inStr.contains(".") || inStr.contains("e") || inStr.contains("E")) {
            return Double.parseDouble(inStr);
          } else {
            // for safety, check the overflow issue, return BigInteger when overflow occurs
            BigInteger num = new BigInteger(inStr);
            BigInteger longMax = new BigInteger(String.valueOf(Long.MAX_VALUE));
            BigInteger longMin = new BigInteger(String.valueOf(Long.MIN_VALUE));
            if (num.compareTo(longMax) > 0 || num.compareTo(longMin) < 0) {
              return num;
            }
          }
          return Long.parseLong(inStr);

        case BOOLEAN:
          return in.nextBoolean();

        case NULL:
          in.nextNull();
          return null;

        default:
          throw new IllegalStateException();
      }
    }

    @SuppressWarnings("unchecked")
    @Override public void write(JsonWriter out, Object value) throws IOException {
      if (value == null) {
        out.nullValue();
        return;
      }

      delegate.write(out, value);
    }
  }

  private GsonObjectBuilder() {

  }

  private static class GsonHolder {
    private static final GsonObjectAdapter ADAPTER = new GsonObjectAdapter();
    private static final TaskSummaryAdapter TS_ADAPTER = new TaskSummaryAdapter();
    private static final Gson GSON_INSTANCE = new GsonBuilder()
            .disableHtmlEscaping()
            .registerTypeAdapter(Map.class, ADAPTER)
            .registerTypeAdapter(new TypeToken>(){}.getType(), ADAPTER)
            .registerTypeAdapter(new TypeToken>(){}.getType(), ADAPTER)
            .registerTypeAdapter(Instance.TaskSummary.class, TS_ADAPTER)
            .create();
  }

  public static Gson get() {
    return GsonHolder.GSON_INSTANCE;
  }
}