使用Gson解析错误json数据

在拉取数据时,有时候会遇到一些错误数据导致整张表都无法正常存储的情况,比如这种错误:

com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: Invalid long: "null"

原json数据是这样的:

            {
    
    
                "id": "1202075683237523457",
                "device_type": "00001001",
                "device_id": "000d6f000a7ef40d",
                "mode_id": "null",
                "cmd_id": "color",
                "cmd_order": null,
                "cmd_papam": "{\"p1\":\"175\",\"p2\":\"240\",\"p3\":\"188\",\"p4\":\"\"}"
            }

但需要序列化的mode_id本应该是一个long型,但由于某种原因,服务端提供的数据中出现了"null"这种无法正常转换为long的数据,那么gson在反序列化json数据的时候,就会出现异常。

这个时候,最好的方法自然是要求服务端校验数据…
当然在无奈的情况下,只能强行解析这样子。

用法

这个时候就需要用到gson的一些特殊用法:

  public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) 

顾名思义,用以注册TypeAdapter的方法,TypeAdapter是gson的核心模块之一,用于转换与解析数据,不可或缺。

在通常情况下,仅需要一行代码就能将json数据转换为想要的Model类型,比如想让将上面的json数据反序列化为一个ModeCmd实例:

    Gson gson = new Gson();
    ModeCmd modeCmd = gson.fromJson(json,new TypeToken<ModeCmd>(){
    
    }.getType());

这种情况下,其实是使用的gson默认配置的TypeAdapter,遇到错误数据的时候,会按正常逻辑抛出异常,这个时候可以写一个JsonDeserializer通过registerTypeAdapter配置一个TypeAdapter,使其在解析ModeCmd数据时,使用JsonDeserializer来解析。
JsonDeserializer可以写成这样:

	private JsonDeserializer<ModeCmd> modeCmdJsonDeserializer = new JsonDeserializer<ModeCmd>() {
    
    
		@Override
		public ModeCmd deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
    
    
			try {
    
    
                json.getAsJsonObject().get("mode_id").getAsLong();
			} catch (Exception ex) {
    
    
                ex.printStackTrace();
                json.getAsJsonObject().addProperty("mode_id", "0");
			}
			return gson.getGson().fromJson(json, typeOfT);
		}
	};

这里是将mode_id的数据强行换为“0”,也可以做其他处理。
最后使用

     Gson gson = new GsonBuilder().registerTypeAdapter(ModeCmd.class, modeCmdJsonDeserializer).create();
     ModeCmd modeCmd = gson.fromJson(json,new TypeToken<ModeCmd>(){
    
    }.getType())

得到的gson去解析数据即可。

原理

之所以可以这么做的缘由,是因为gson解析数据也是遍历JsonTree一层一层选择合适TypeAdapter去解析当前层数据的,并且gson在新建时(构造方法)已经内置了大量TypeAdapter:

//Gson
    List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();

    // built-in type adapters that cannot be overridden
    factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
    factories.add(ObjectTypeAdapter.FACTORY);

    // the excluder must precede all adapters that handle user-defined types
    factories.add(excluder);

    // user's type adapters
    factories.addAll(typeAdapterFactories);

    // type adapters for basic platform types
    factories.add(TypeAdapters.STRING_FACTORY);
    factories.add(TypeAdapters.INTEGER_FACTORY);
    factories.add(TypeAdapters.BOOLEAN_FACTORY);
    factories.add(TypeAdapters.BYTE_FACTORY);
    factories.add(TypeAdapters.SHORT_FACTORY);
    TypeAdapter<Number> longAdapter = longAdapter(longSerializationPolicy);
    factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter));
    factories.add(TypeAdapters.newFactory(double.class, Double.class,
            doubleAdapter(serializeSpecialFloatingPointValues)));
    factories.add(TypeAdapters.newFactory(float.class, Float.class,
            floatAdapter(serializeSpecialFloatingPointValues)));
    factories.add(TypeAdapters.NUMBER_FACTORY);
    factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY);
    factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY);
    factories.add(TypeAdapters.newFactory(AtomicLong.class, atomicLongAdapter(longAdapter)));
    factories.add(TypeAdapters.newFactory(AtomicLongArray.class, atomicLongArrayAdapter(longAdapter)));
    factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_FACTORY);
    factories.add(TypeAdapters.CHARACTER_FACTORY);
    factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
    factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
    factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL));
    factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER));
    factories.add(TypeAdapters.URL_FACTORY);
    factories.add(TypeAdapters.URI_FACTORY);
    factories.add(TypeAdapters.UUID_FACTORY);
    factories.add(TypeAdapters.CURRENCY_FACTORY);
    factories.add(TypeAdapters.LOCALE_FACTORY);
    factories.add(TypeAdapters.INET_ADDRESS_FACTORY);
    factories.add(TypeAdapters.BIT_SET_FACTORY);
    factories.add(DateTypeAdapter.FACTORY);
    factories.add(TypeAdapters.CALENDAR_FACTORY);
    factories.add(TimeTypeAdapter.FACTORY);
    factories.add(SqlDateTypeAdapter.FACTORY);
    factories.add(TypeAdapters.TIMESTAMP_FACTORY);
    factories.add(ArrayTypeAdapter.FACTORY);
    factories.add(TypeAdapters.CLASS_FACTORY);

    // type adapters for composite and user-defined types
    factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
    factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
    this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor);
    factories.add(jsonAdapterFactory);
    factories.add(TypeAdapters.ENUM_FACTORY);
    factories.add(new ReflectiveTypeAdapterFactory(
        constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory));

    this.factories = Collections.unmodifiableList(factories);

包括了JsonElement/Integer/Long各种基础类型相关的TypeAdapter,比如解析字符串使用的是STRING_FACTORY

  public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING);

  public static final TypeAdapter<StringBuilder> STRING_BUILDER = new TypeAdapter<StringBuilder>() {
    
    
    @Override
    public StringBuilder read(JsonReader in) throws IOException {
    
    
      if (in.peek() == JsonToken.NULL) {
    
    
        in.nextNull();
        return null;
      }
      return new StringBuilder(in.nextString());
    }
    @Override
    public void write(JsonWriter out, StringBuilder value) throws IOException {
    
    
      out.value(value == null ? null : value.toString());
    }
  };

而当我们使用gson.fromJson方法时,就会在factories中挑选合适的TypeAdapter去解析数据:

//Gson.java
  public <T> T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException {
    
    
    if (json == null) {
    
    
      return null;
    }
    return (T) fromJson(new JsonTreeReader(json), typeOfT);
  }

  public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
    
    
	//略
      TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
      TypeAdapter<T> typeAdapter = getAdapter(typeToken);
      T object = typeAdapter.read(reader);
      return object;
    //略
  }

  public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
    
    
    //略
    try {
    
    
      FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
      threadCalls.put(type, call);

      for (TypeAdapterFactory factory : factories) {
    
    
        TypeAdapter<T> candidate = factory.create(this, type);
        if (candidate != null) {
    
    
          call.setDelegate(candidate);
          typeTokenCache.put(type, candidate);
          return candidate;//取得合适的TypeAdapter
        }
      }
      throw new IllegalArgumentException("GSON cannot handle " + type);
    } finally {
    
    
      //略
    }
  }

可以在getAdapter方法看到,当json数据传入时会遍历factories并去取得对应的TypeAdapter,而后去解析。
所以当我们使用registerTypeAdapter方法时,其实就是手动添加了一个TypeAdapter以供选择:

//GsonBuilder.java
  public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
    
    
    $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
        || typeAdapter instanceof JsonDeserializer<?>
        || typeAdapter instanceof InstanceCreator<?>
        || typeAdapter instanceof TypeAdapter<?>);
    if (typeAdapter instanceof InstanceCreator<?>) {
    
    
      instanceCreators.put(type, (InstanceCreator) typeAdapter);
    }
    if (typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?>) {
    
    
      TypeToken<?> typeToken = TypeToken.get(type);
      factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter));
    }
    if (typeAdapter instanceof TypeAdapter<?>) {
    
    
      factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter));
    }
    return this;
  }

最终解析的时候,会走到调用deserialize的地方,也是JsonDeserializer之所以生效的源头:

//TreeTypeAdapter.java
  @Override public T read(JsonReader in) throws IOException {
    
    
    if (deserializer == null) {
    
    
      return delegate().read(in);
    }
    JsonElement value = Streams.parse(in);
    if (value.isJsonNull()) {
    
    
      return null;
    }
    return deserializer.deserialize(value, typeToken.getType(), context);
  }

以上。

猜你喜欢

转载自blog.csdn.net/ifmylove2011/article/details/103405688