My JSON response (from a server) has attributes that are JSON objects, but I don't want to parse them all, instead I want to keep some of them as a JSON-encoded String.
For instance:
{
"date": "23-03-2019",
"changed": true,
"data": {
"login": "9999999",
"loginFormatted": "999 99 99",
}
}
Here I want to parse "data" attribute as string. How can I do it? (I am using Retrofit v2.4.0 and Moshi v1.5.0)
My model class for response:
public class Response {
@Json(name = "date")
public long date;
@Json(name = "changed")
public boolean changed;
@Json(name = "data")
public String data;
}
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 DataString
internal 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With