0

I'm trying to serialize an android.print.PrinterInfo object to JSON using Gson (version 2.9.0):

val type: Class<*> = data.javaClass
json.beginObject()
json.name(type.name)
json.value(gson.toJson(data, type))
Log.d(TAG, data.toString())
json.endObject()

but the result is an empty object {android.print.PrinterInfo: {}}. The log line clearly shows that the object is populated, there are no null values or similar (but using GsonBuilder() to include nulls, to pretty print or anything else doesn't make any difference). There are no exceptions, just no expected result.

5
  • Android Studio for some reason didn't inform about newer versions. Now I tried with the latest 2.10.1, no difference. Commented Nov 3, 2023 at 12:54
  • What will be the output of data.toString()? Commented Nov 3, 2023 at 22:42
  • PrinterInfo has a nice toString(), that emits a longish string in the format of "PrinterInfo{id=...,name=...,status=...}" Actually, I can solve the problem somewhat by providing a custom JsonSerializer for every single type involved (considering that these are nested structures, this means quite a lot of custom serializers, of course). But does that mean that Gson is practically unable to serialize anything but the simplest object with basic types? Commented Nov 4, 2023 at 0:09
  • Could you please include the full code you use to create the JSON and the logging call? I assume json is a JsonWriter, but your code does not include that, neither does it show where you log the JSON data. And {android.print.PrinterInfo: {}} in your question is not JSON (it is missing " around the member name); so maybe there could also be an issue in the way you create the JSON data? Commented Nov 5, 2023 at 15:51
  • I'll comment about the merits of the case below the answer but for the sake of completeness, you see the complete code except for a single call val json = Gson(). The rest compiles and works as a complete, standalone unit. Commented Nov 5, 2023 at 17:41

1 Answer 1

0

The main problem seems to be that you are trying to serialize a class from the Android library without using a custom Gson adapter for this. In that case Gson will use reflection to access the fields of the class, including private fields which represent implementation details.

Even if this worked and you got a non-empty JSON object, it would contain internal data of the class, so it might differ between Android versions (maybe even between different Android devices?). And there is no guarantee that you can properly recreate a PrinterInfo object using Gson.fromJson this way. Possibly the PrinterInfo you would receive is in an inconsistent state and non-functional.

A better solution here would be either to write a custom Gson TypeAdapter for the PrinterInfo class (and referenced classes) which performs the serialization (and deserialization).
Or because PrinterInfo implements Parcelable another option might be to use its writeToParcel method instead of serializing it as JSON.

In the future you can also use Gson's ReflectionAccessFilter to avoid depending on reflection-based serialization of Android library classes by accident.


Arguably this does not answer why the JSON object in your question is empty, but it highlights that trying to (implicitly) use reflection for JSON serialization of third-party classes has its disadvantages, and that there are other alternatives.

Sign up to request clarification or add additional context in comments.

7 Comments

Yes, that's what I ended up doing, it required no less than ten separate type adapters. Considering that all of them are relatively straightforward data types, that was exactly what I had originally expected, Gson doing it for me. Mostly because its documentation clearly mentions that private fields are not only not a problem but explicitly recommended: github.com/google/gson/blob/main/… From that documentation, I had the -- probably false, I reckon -- impression that it's capable of doing it all by itself ...
... and I only need to provide my own adapters if I want to do something differently than the stock conversion. Actually, the rest is not that important. I don't particularly care about compatiblity with later Android versions because I don't store the data. I need the conversion in order to send it between two different parts of the same app in the present. And the two have different engines (Android/Kotlin and Flutter/Dart), so using parcels was out of question, too.
Gson should actually be able to serialize the PrinterInfo fields (it serializes anonymous and local classes as null, but that should not apply here). So Log.d(TAG, Gson().toJson(data)) should give the desired result; I am not sure why it doesn't. But still I would not recommend to use it on PrinterInfo (or any third-party classes you don't control). The Gson user guide probably mentions private fields in the context of your own classes, whose structure you control.
Even using Gson's reflection-based serialization only temporarily at runtime could cause issues I guess when you use Gson.fromJson. If there are any fields whose values needs to be recomputed after creation and any classes which don't have a no-args constructor (see GsonBuilder.disableJdkUnsafe()) you might get broken instances after deserialization.
Could well be. But I don't plan on using fromJson() at all, I only need the conversion in one direction. Actually, in both directions, I have to send the data but the other side will create its own objects from scratch. It will pick and use data from the JSON but it will use standard object creation and not deserialization.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.