Niclas
Niclas

Reputation: 539

How can I use auto-value-gson to map unknown json fields

I am making a simple github gist view app in android that talks with their api.

But I am unsure how I can use AutoValue and GSON to map this json response. Do note that I have removed alot of keys since I dont need it currently, the actual response looks like this.

Example Response

[
  {
    "id": "3937809cf12adae05595c43c5ef4ce56",
    "files": {
      "main.css": {
        "filename": "main.css",
        "type": "text/css",
        "language": "CSS",
        "raw_url": "https://gist.githubusercontent.com/*/3937809cf12adae05595c43c5ef4ce56/raw/f46b97a4cf561fa18e50e14bed734eea78bc58d9/main.css",
        "size": 102
      },
      "readme.txt": {
        "filename": "readme.txt",
        "type": "text/plain",
        "language": "Text",
        "raw_url": "https://gist.githubusercontent.com/*/3937809cf12adae05595c43c5ef4ce56/raw/aa7bf7046baaf58b23b4fa4c3b19a575d5eae36e/readme.txt",
        "size": 192
      }
    }
  }
]

Question I have read that I could map between String and File object to get it to work. But it does not work since files key is or can be an object of objects.

So how can I map these object of unknown objects with auto-value-gson? Is it even possible?

Code

GistResponse.java

@AutoValue
public abstract class GistResponse {

    @SerializedName("id")
    public abstract String getId();

    @SerializedName("files")
    public abstract FileName getFileNameList();

    public static Builder builder() {
        return new AutoValue_GistResponse.Builder();
    }

    @AutoValue.Builder
    public abstract static class Builder {
        public abstract Builder setId(String value);
        public abstract Builder setFileNameList(FileName value);

        public abstract GistResponse build();
    }

    public static TypeAdapter<GistResponse> typeAdapter(Gson gson) {
        return new AutoValue_GistResponse.GsonTypeAdapter(gson);
    }

}

FileName.java

@AutoValue
public abstract class FileName {

    public abstract Map<String, File> getFilesList();

    public static Builder builder() {
        return new AutoValue_FileName.Builder();
    }

    @AutoValue.Builder
    public abstract static class Builder {
        public abstract Builder setFilesList(Map<String, File> value);
        public abstract FileName build();
    }

    public static TypeAdapter<FileName> typeAdapter(Gson gson) {
        return new AutoValue_FileName.GsonTypeAdapter(gson);
    }
}

File.java

@AutoValue
public abstract class File {

    @SerializedName("filename")
    public abstract String getFileName();

    @SerializedName("type")
    public abstract String getType();

    @SerializedName("language")
    public abstract String getLanguage();

    @SerializedName("raw_url")
    public abstract String getRawUrl();

    @SerializedName("size")
    public abstract Integer getSize();

    public static Builder builder() { return new AutoValue_File.Builder(); }

    @AutoValue.Builder
    public abstract static class Builder {
        public abstract Builder setFileName(String value);
        public abstract Builder setType(String value);
        public abstract Builder setLanguage(String value);
        public abstract Builder setRawUrl(String value);
        public abstract Builder setSize(Integer value);

        public abstract File build();
    }

    public static TypeAdapter<File> typeAdapter(Gson gson) {
        return new AutoValue_File.GsonTypeAdapter(gson);
    }
}

Related topics and information:

Upvotes: 1

Views: 1498

Answers (1)

Niclas
Niclas

Reputation: 539

I have since found a solution which consists of writing a custom TypeAdapter which was actually pretty easy.

Here is the code:

public class FileTypeAdapter extends TypeAdapter {

    @Override
    public void write(JsonWriter out, Object value) throws IOException {}

    @Override
    public List<File> read(JsonReader jsonReader) throws IOException {

        ArrayList<File> fileList = new ArrayList<>();

        if (jsonReader.peek() == JsonToken.NULL) {
            jsonReader.nextNull();
            return fileList;
        }

        jsonReader.beginObject();
        while (jsonReader.hasNext()) {

            jsonReader.nextName();

            if (jsonReader.peek() == JsonToken.NULL) {
                jsonReader.nextNull();
                continue;
            }

            File.Builder file = File.builder();

            jsonReader.beginObject();
            while (jsonReader.hasNext()) {

                String nextName = jsonReader.nextName();

                switch (nextName) {
                    case "filename": {
                        String nextString = jsonReader.nextString();
                        file.setFileName(nextString);
                        break;
                    }
                    case "type": {
                        String nextString = jsonReader.nextString();
                        file.setType(nextString);
                        break;
                    }
                    case "language": {

                        if (jsonReader.peek() == JsonToken.NULL) {
                            jsonReader.nextNull();
                        } else {
                            String nextString = jsonReader.nextString();
                            file.setLanguage(nextString);
                        }
                        break;
                    }
                    case "raw_url": {
                        String nextString = jsonReader.nextString();
                        file.setRawUrl(nextString);
                        break;
                    }
                    case "size": {
                        Integer nextInt = jsonReader.nextInt();
                        file.setSize(nextInt);
                        break;
                    }
                    default: {
                        jsonReader.skipValue();
                    }
                }
            }
            fileList.add(file.build());
            jsonReader.endObject();
        }
        jsonReader.endObject();
        return fileList;
    }
}

Then just annotate the files field in with @GsonTypeAdapter inside GistResponse.java.

@AutoValue
public abstract class GistResponse {

    @SerializedName("id")
    public abstract String getId();

    @GsonTypeAdapter(FileTypeAdapter.class)
    @SerializedName("files")
    public abstract FileName getFileNameList();

    public static Builder builder() {
        return new AutoValue_GistResponse.Builder();
    }

    @AutoValue.Builder
    public abstract static class Builder {
        public abstract Builder setId(String value);
        public abstract Builder setFileNameList(FileName value);

        public abstract GistResponse build();
    }

    public static TypeAdapter<GistResponse> typeAdapter(Gson gson) {
        return new AutoValue_GistResponse.GsonTypeAdapter(gson);
    }

}

And now its possible to parse it.

Upvotes: 1

Related Questions