Using Gson to parse wrong json data

When pulling data, sometimes some wrong data will cause the entire table to fail to be stored normally, such as this error:

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

The original json data is like this:

            {
    
    
                "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\":\"\"}"
            }

But the mode_id that needs to be serialized should be a long type, but for some reason, "null" appears in the data provided by the server, which cannot be converted to long normally, so when gson deserializes the json data , an exception will occur.

At this time, the best way is naturally to ask the server to verify the data...
Of course, in desperation, it can only be forcibly parsed like this.

usage

At this time, some special usages of gson are needed:

  public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) 

As the name implies, it is used to registerTypeAdapterThe method, TypeAdapter is one of the core modules of gson, which is used to convert and parse data and is indispensable.

Under normal circumstances, only one line of code is needed to convert json data to the desired Model type, for example, if you want to deserialize the above json data into a ModeCmd instance:

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

In this case, the TypeAdapter configured by gson is actually used by default. When encountering wrong data, an exception will be thrown according to the normal logic. At this time, you can write a JsonDeserializer to configure a TypeAdapter through registerTypeAdapter , so that when parsing ModeCmd data, Use JsonDeserializer to parse.
JsonDeserializer can be written like this:

	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);
		}
	};

Here is to forcibly change the data of mode_id to "0", and other processing can also be done.
last used

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

The obtained gson can parse the data.

principle

The reason why this can be done is that gson parses data by traversing the JsonTree layer by layer to select the appropriate TypeAdapter to parse the current layer data, and gson has built a large number of TypeAdapters when it is newly created (construction method):

//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);

Includes TypeAdapter related to various basic types of JsonElement/Integer/Long, for example, STRING_FACTORY is used to parse strings :

  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());
    }
  };

And when we use the gson.fromJson method, we will select the appropriate TypeAdapter in the factories to parse the data:

//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 {
    
    
      //略
    }
  }

You can see in the getAdapter method that when the json data is passed in, it will traverse the factories and obtain the corresponding TypeAdapter, and then parse it.
So when we use the registerTypeAdapter method, we actually manually add a TypeAdapter for selection:

//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;
  }

In the final parsing, it will go to the place where deserialize is called, which is also the source of why JsonDeserializer takes effect:

//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);
  }

that's all.

Guess you like

Origin blog.csdn.net/ifmylove2011/article/details/103405688