How to (de)serialize an instance of a generated data class - kotlin

I need to (de)serialize to and from JSON some of the data classes that are generated by a Gradle plugin. Normally I would just use a library like Moshi or kotlinx.serialization and add the proper annotation to the class I want to serialize but, since this data classes are autogenerated, this is a problem.
I would like to avoid manually to map all the fields of the generated data class to some other class that I can (de)serialize, or to write a custom adapter for all these data class so, I was wondering if there is another way to tell, for example, kotlinx.serialization that a class is #Serializable without having to put the annotation directly on top of the class itself.
Or, alternatively, is there a better way to convert to and from a string an instance of a generated data class?

kotlinx.serialization supports generating serializers for 3rd party classes. We need to use forClass parameter in #Serializer, for example:
data class MyData(val data: String)
#Serializer(forClass = MyData::class)
object MyDataSerializer
fun main() {
val data = MyData("foo")
println(Json.encodeToString(MyDataSerializer, data))
}
You can read more in the official documentation: https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serializers.md#deriving-external-serializer-for-another-kotlin-class-experimental

Related

Serialize list of objects with json_serializable without creating extra class

I saw a tutorial where an extra class is created just to be able to serialize a list of objects, instead of a single object:
I'm using json_serializable to generate some serialization code for my class Preference, but now I want to save a list of preferences using shared_preferences and I get an error obviously.
var sSavedPrefs = json.encode(PreferenceRepo.getSavedPrefs());
prefs.setString(saved_prefs_key, sSavedPrefs );
I used
#JsonSerializable()
class Preference{...}
to make it serializable, but I don't want to create an extra class like
#JsonSerializable()
class Preferences{...}
just to make it work - is there a better way?
I found a way:
Using the setStringList method I could create a List where I added each serialized object one by one without needing an extra list class. I also noticed that json.encode might not have been the right method to use, I saw jsonEncode in another tutorial and used it instead:
List<String> savedPrefsJson = [];
for (Preference savedPref in PreferenceRepo.savedPrefs) {
String savedPrefJson = jsonEncode(savedPref);
savedPrefsJson.add(savedPrefJson);
}
prefs.setStringList(saved_prefs_key, savedPrefsJson);

A proper way to serialize/deserialize Xodus-dnq entity

For example - I've got this entity:
class XdUser(entity: Entity) : XdEntity(entity) {
var someName by xdStringProp()
var someNumber by xdIntProp()
}
What is the proper way to serialize/deserialize it from/to json?
I have to create data class which just duplicates my entity's fields and then propagate values to XdUser? Or there is other way?
Serializing XdUser to JSON you should be sure that serializer won't process XdUser#entity and other public links which can expose large amount of unnecessary data. Deserialization brings another problems because deserializator should be informed about how to instantiate a class from json using constructor XdUser(entity: Entity).
From my prospective better choice there is to have another level for rest API. It brings ability to control amount of exposed data, control permissions (if your have them) and the way of how entities will be updated.
Rest api level be implemented like this:
open class EntityVO<T: XdEntity>(xdId: String?) {
}
class UserVO(xdId: String?): EntityVO<XdUser>(xdId: String?) {
var someName by delegateTo(XdUser::someName)
var someNumber by delegateTo(XdUser::someNumber)
}
and delegateTo should provide delegate which will lookup XdUser by xdId and do get/set value using specified XdUser property. For link/links logic will be more complex but idea can be the same.

Kotlin: Scoping using Object vs Class

I have a few data classes, that is short, so I group them together in a file.
I can defined them in a Kotlin file as it is, but would prefer it to be scope within a class/object, so the file is not just a file, but under class/object for better grouping
I could do
object Model {
data class Result(val query: Query)
data class Query(val searchinfo: SearchInfo)
data class SearchInfo(val totalhits: Int)
}
and I could also do
class Model {
data class Result(val query: Query)
data class Query(val searchinfo: SearchInfo)
data class SearchInfo(val totalhits: Int)
}
They both looks the same to me. What's the different, and if there's a preferred way in term of scoping my data classes?
I would advise against using classes for scoping other classes. As Todd explains in his answer, you can use sealed classes which offer an actual benefit of exhaustive when checks. If you don't need this feature, Kotlin has a built-in mechanism for scoping - packages:
package org.company.model
data class Result(val query: Query)
data class Query(val searchinfo: SearchInfo)
data class SearchInfo(val totalhits: Int)
I can defined them in a Kotlin file as it is, but would prefer it to be scope within a class/object, so the file is not just a file, but under class/object for better grouping
There's nothing wrong with a file containing multiple top-level elements. This is a useful language feature and is used in exactly this kind of situation.
Another option is to make all of your data classes a subclass of a sealed Model class. This will give you the benefit of defining them all in one place, because Kotlin enforces that for sealed classs. Also, having the type system know about all instances of type Model is helpful in when expressions as well because then it won't require you to put an else -> block.
sealed class Model
data class Result(val query: Query) : Model()
data class Query(val searchinfo: SearchInfo) : Model()
data class SearchInfo(val totalhits: Int) : Model()
And you can just use them directly:
val r = Result(Query(SearchInfo(3))
Instead of wrapping them in another class or object, where you'd have to refer to them like this:
val r = Model.Result(Model.Query(Model.SearchInfo(3)))

Dynamic Schema & Deserialization with Protostuff

I'm using Protostuff in an attempt to serialize/deserialize objects of several different types for which no protobuf sources are available (it's a server-server RPC scenario). Serialization goes OK because I know the type of the object to serialize and can create the schema:
Schema schema = RuntimeSchema.getSchema(object.getClass());
Now, I use ProtobufIOUtil.toByteArray and get a byte array which I then pass to a remote server. However, I can't seem to deserialize this byte array in the remote server because I have no way to create a schema for an object of "unknown" type. Is there any way I can get past this and use Protostuff in the same way I would use Java's native serialization?
There are few solutions with common idea - serialize name of the class together with the data.
First one requires protostuff-runtime. You should create wrapper class with one field of type Object:
public class Wrapper {
public Object data;
}
Then you put your object to data field and serialize wrapper, protostuff-runtime will append class name to serialized form automatically, and later use it for deserialization.
If you want more control, then you can do similar thing without protistuff-runtime.
First, you need a wrapper class:
public class Wrapper {
public String clazz;
public byte[] data;
}
Then you should serialize your data to byte array, store it to wrapper, and then serialize wrapper instance.
On remote side, you deserialize Wrapper first, then get clazz field - it is the class you should use to deserialize data.

Transforming an object hierarchy using Jackson

I would like to leverage the Jackson infrastructure to transform an untyped Map/List structure into a POJO hierarchy.
What I need is pretty close to
jsonNode = objectMapper.valueToTree(MapHierarchy) followed by
pojoTree = objectMapper.treeToValue(jsonNode)
...except that I need to modify the field names on the fly: the names found in the source Map/List hierarchy are declared as custom annotations on the POJO class.
Here is a complete example:
public class Cat
{
#MapField("attr:col")
#JsonProperty("color")
private String _color;
}
... and the source Map:
{
"attr:col" -> "black"
}
... from which I would like to obtain a Cat instance.
I am sure there has got to be an elegant way to achieve this with the Jackson APIs, but I can't quite figure out how... any suggestions? Obviously I don't want to create a second parallel class hierarchy just to describe the deserialization of the Map through annotations.
An extra bonus would be to be able to transform the Map without going through stringified JSon which I believe is how valueToTree works.