How to stop Moshi from parsing a specific object attribute
When Moshi looks at the hierarchy of Response
class, it decides to use a JsonAdapter<String>
to parse the field data
. So the solution is to tell Moshi not to use JsonAdapter<String>
to parse it but delegate the task to our JsonAdapter
.
Talk is cheap, here is the code.
class KeepAsJsonString { public void run() throws Exception { String json = "" + "{\n" + "\"date\": \"23-03-2019\",\n" + "\"changed\": true,\n" + "\"data\": {\n" + " \"login\": \"9999999\",\n" + " \"loginFormatted\": \"999 99 99\"\n" + " }\n" + "}"; Moshi moshi = new Moshi.Builder().add(new DataToStringAdapter()).build(); JsonAdapter<Response> jsonAdapter = moshi.adapter(Response.class); Response response = jsonAdapter.fromJson(json); System.out.println(response.data); // {"login":"9999999","loginFormatted":"999 99 99"} } static class Response { @Json(name = "date") public String date; @Json(name = "changed") public boolean changed; // Ask moshi to forward the intermediate result to some function with a String annotated with @DataString, // in our case, DataToStringAdapter.fromJson() and DataToStringAdapter.toJson() @Json(name = "data") public @DataString String data; } @Retention(RUNTIME) @JsonQualifier public @interface DataString { } static class DataToStringAdapter { @ToJson void toJson(JsonWriter writer, @DataString String string) throws IOException { // Write raw JSON string writer.value(new Buffer().writeUtf8(string)); } @FromJson @DataString String fromJson(JsonReader reader, JsonAdapter<Object> delegate) throws IOException { // Now the intermediate data object (a Map) comes here Object data = reader.readJsonValue(); // Just delegate to JsonAdapter<Object>, so we got a JSON string of the object return delegate.toJson(data); } } public static void main(String[] args) throws Exception { new KeepAsJsonString().run(); }}
In Kotlin it can look like this:
@JsonQualifier@Retention(AnnotationRetention.RUNTIME)@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FUNCTION)annotation class DataStringinternal class JsonObjectToStringJsonAdapter { @ToJson fun toJson(@DataString s: String): String { return s } @FromJson @DataString fun fromJson(reader: JsonReader, adapter: JsonAdapter<Any>): String { val jsonObject: Any = reader.readJsonValue()!! return adapter.toJson(jsonObject) }}
Update:
As Eric Cochran mentioned, there will be a more efficient way (JsonReader.readJsonString()
) to read JSON as string when this issue is fixed.