Dealing with one field that is sometimes boolean and sometimes int

人盡茶涼 提交于 2020-05-14 03:58:10

问题


I'm trying to work with the reddit JSON API. There are post data objects that contain a field called edited which may contain a boolean false if the post hasn't been edited, or a timestamp int if the post was edited.

Sometimes a boolean:

{ "edited": false, "title": "Title 1" }

Sometimes an int: { "edited": 1234567890, "title": "Title 2" }

When trying to parse the JSON where the POJO has the field set to int, I get an error: JsonDataException: Expected an int but was BOOLEAN...

How can I deal with this using Moshi?


回答1:


I also ran into a similar problem where I had fields that were sometimes booleans, and sometimes ints. I wanted them to always be ints. Here's how I solved it with Moshi and kotlin:

  1. Make a new annotation that you will use on fields to should convert from boolean to int

    @JsonQualifier
    @Retention(AnnotationRetention.RUNTIME)
    @Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FUNCTION)
    annotation class ForceToInt

    internal class ForceToIntJsonAdapter {
        @ToJson
        fun toJson(@ForceToInt i: Int): Int {
            return i
        }

        @FromJson
        @ForceToInt
        fun fromJson(reader: JsonReader): Int {
            return when (reader.peek()) {
                JsonReader.Token.NUMBER -> reader.nextInt()
                JsonReader.Token.BOOLEAN -> if (reader.nextBoolean()) 1 else 0
                else -> {
                    reader.skipValue() // or throw
                    0
                }
            }
        }
    }

  1. Use this annotation on the fields that you want to force to int:
    @JsonClass(generateAdapter = true)
    data class Discovery(
            @Json(name = "id") val id: String = -1,
            @ForceToInt @Json(name = "thanked") val thanked: Int = 0
    )



回答2:


The easy way might be to make your Java edited field an Object type. The better way for performance, error catching, and appliaction usage is to use a custom JsonAdapter.

Example (edit as needed):

public final class Foo {
  public final boolean edited;
  public final int editedNumber;
  public final String title;

  public static final Object JSON_ADAPTER = new Object() {
    final JsonReader.Options options = JsonReader.Options.of("edited", "title");

    @FromJson Foo fromJson(JsonReader reader) throws IOException {
      reader.beginObject();
      boolean edited = true;
      int editedNumber = -1;
      String title = "";
      while (reader.hasNext()) {
        switch (reader.selectName(options)) {
          case 0:
            if (reader.peek() == JsonReader.Token.BOOLEAN) {
              edited = reader.nextBoolean();
            } else {
              editedNumber = reader.nextInt();
            }
            break;
          case 1:
            title = reader.nextString();
            break;
          case -1:
            reader.nextName();
            reader.skipValue();
          default:
            throw new AssertionError();
        }
      }
      reader.endObject();
      return new Foo(edited, editedNumber, title);
    }

    @ToJson void toJson(JsonWriter writer, Foo value) throws IOException {
      writer.beginObject();
      writer.name("edited");
      if (value.edited) {
        writer.value(value.editedNumber);
      } else {
        writer.value(false);
      }
      writer.name("title");
      writer.value(value.title);
      writer.endObject();
    }
  };

  Foo(boolean edited, int editedNumber, String title) {
    this.edited = edited;
    this.editedNumber = editedNumber;
    this.title = title;
  }
}

Don't forget to register the adapter on your Moshi instance.

Moshi moshi = new Moshi.Builder().add(Foo.JSON_ADAPTER).build();
JsonAdapter<Foo> fooAdapter = moshi.adapter(Foo.class);


来源:https://stackoverflow.com/questions/48918990/dealing-with-one-field-that-is-sometimes-boolean-and-sometimes-int

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!