what is the most efficient way to serialize primitive type array by protostuff - protostuff

There is no example of how to serialize primitive type array by protostuff. I also want to know which way is the most efficient.
At first, I write code as follow:
long[] array = {1L, 2L, 3L};
Schema<long[]> schema = RuntimeSchema.getSchema(long[].class);
ProtobufIOUtil.toByteArray(array, schema, buffer);
the exception is:
Exception in thread "main" java.lang.RuntimeException: The root object can neither be an abstract class nor interface: "[J
at io.protostuff.runtime.RuntimeSchema.createFrom(RuntimeSchema.java:210)
at io.protostuff.runtime.RuntimeSchema.createFrom(RuntimeSchema.java:187)
at io.protostuff.runtime.IdStrategy.newSchema(IdStrategy.java:116)
at io.protostuff.runtime.DefaultIdStrategy$Lazy.getSchema(DefaultIdStrategy.java:708)
at io.protostuff.runtime.RuntimeSchema.getSchema(RuntimeSchema.java:149)
at io.protostuff.runtime.RuntimeSchema.getSchema(RuntimeSchema.java:140)
at kryo.demo.UserCompareKryoAndProtostuff.testProtostuff(UserCompareKryoAndProtostuff.java:66)
at kryo.demo.UserCompareKryoAndProtostuff.main(UserCompareKryoAndProtostuff.java:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
I read the ArraySchemas code quickly, I don't get any idea.

Related

How can I encode a typed class with Kotlinx Serialization?

I'd like to encode a given class of type T: EventData with Kotlinx Serialization encodeToString.
This is my code:
class EventDispatcher<T: EventData>(
val pubSubTemplate: PubSubTemplate
) {
/**
* Dispatch an event to the game engine event manager pipeline
*/
fun dispatchEvent(event: T, initiator: String) {
val eventData: String = Json.encodeToString(event)
}
The compiler tells me:
Cannot use `T` as reified type parameter. Use a class instead
Is there a way to make this still work?
For Json.encodeToString(event) to work, it needs the type information for T. But, this type information is lost at runtime due to the way how generics work in Kotlin/Java.
One way to retain the type information would be by making dispatchEvent an inline function with T as a reified type parameter.
However, this also raises the question how you want to serialize event. You could also use polymorphic serialization of EventData, rather than trying to serialize T. This will include an additional class discriminator in your serialized output (it necessarily has to for polymorphic serialization/deserialization to work).
If you serialize the concrete type T, this class discriminator wouldn't be included, which is questionable; how would whoever will deserialize this know what type it is?
In short, I think you need polymorphic serialization.

How to deserialize json to generic type in Kotlin using Jackson/kotlinx.serialization

I have a generic class class MyClass<T> : MyInterface<T> and I want to deserialize a json to generic type T. I tried using Jackson and kotlinx.serialization libraries to deserialize json but I get following error
cannot use T as reified type parameter. Use class instead.
My understanding of why this is happening is because both Jackson and kotlinx deserialize function expect reified T but in my class there is no way to know the type of T at compile time. Is my understanding of this error correct? Is there any way to resolve this error?
My code snippet
class MyClass<T> : MyInterface<T>{
.... <some code> ...
fun readFromJson(json: String){
val obj = jacksonObjectMapper().readValue<T>(json)
// same error if I use kotlinx Json.decodeFromString<T>(json)
...
}
.... <some code> ...
}
My understanding of why this is happening is because both Jackson and kotlinx deserialize function expect reified T but in my class there is no way to know the type of T at compile time. Is my understanding of this error correct?
Correct.
Is there any way to resolve this error?
It depends on what you're trying to do with the T in question. The best would be to lift readFromJson() out of this class, to a place where T can actually be reified.
If you really do need this function to be present in your class (e.g. you need to access some internal state or something), then you'll have to pass a KClass<T>/Class<T> (for Jackson) or a DeserializationStrategy<T> (for Kotlinx serialization) to the constructor of your class, so that you can use the non-reified overloads of readValue() or decodeFromString() which take this extra info as parameter.

Jackson ignores data class fields that aren't boolean but are of names starting with "is"

I'm using Jackson with Kotlin binding in my project. We have a data class that has a field of type Map<A, B> and is named "isRecommended". When Jackson serializes the data class, this field gets omitted in the resultant JSON string.
A simple test to reproduce the same behavior:
class FooKotlin {
#Test
fun testFoo() {
println(jacksonObjectMapper().writeValueAsString(Foo1(true)))
println(jacksonObjectMapper().writeValueAsString(Foo2(1)))
println(jacksonObjectMapper().writeValueAsString(Foo3("true")))
}
}
data class Foo1(val isFoo: Boolean)
data class Foo2(val isFoo: Int)
data class Foo3(val isFoo: String)
The console prints:
{"foo":true}
{}
{}
When I decompile the Kotlin bytecode, the three classes seem to have almost identical content except for the type of the field. So what is the cause of this behavior of Jackson?
As mentioned by #chrsblck it is related to the jackson-module-kotlin issue #80
On the version 2.10.1 it's not reproducible, although serialized properties names are different (the "is" prefix is not removed):
{"isFoo":true}
{"isFoo":1}
{"isFoo":"true"}
On the earlier versions, the issue can be fixed with a JsonProperty annotation:
data class Foo1(val isFoo: Boolean)
data class Foo2(#get:JsonProperty("foo") val isFoo: Int)
data class Foo3(#get:JsonProperty("foo") val isFoo: String)
{"foo":true}
{"foo":1}
{"foo":"true"}
Technically, naming a non-boolean property "isSomthing" is incorrect and violates JavaBeans specification. Jackson relies on the JavaBeans conventions, thus it gets confused.
If you can avoid such naming, I would advise doing so. Otherwise, you may face the same problems when calling the Foo* classes from Java code.

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.

CIL instruction "isinst <valuetype>"

The ECMA Common Language Infrastructure documentation says this about the CIL "isinst class" instruction:
Correct CIL ensures that class is a valid typeref or typedef or typespec token indicating a class, and
that obj is always either null or an object reference.
This implies that a valuetype is not allowed, right? But mscorlib.dll contains a method System.RuntimeTypeHandle::Equals(object obj) with the following instruction:
IL_0001: isinst System.RuntimeTypeHandle
And System.RuntimeTypeHandle is a valuetype. Can anybody put me right here?
Have a look at the declaration of RuntimeTypeHandle:
.class public sequential ansi serializable sealed beforefieldinit RuntimeTypeHandle
extends System.ValueType
implements System.Runtime.Serialization.ISerializable
Although RuntimeTypeHandle is declared as a struct its representation in CIL is some kind of special class. In other words, you can imagine structs as special classes that inherit from System.ValueType and whose attributes follow a strict order.
With that in mind isinst would be callable with RuntimeTypeHandle. For what I interpret isinst is not limited to reference types at all as long as there is a class representing the type.
Let's say we write in C#:
var i = 4;
var b = i is Int32;
We get a compiler warning
Warning: The given expression is always of the provided ('int') type.
What happens? We assign 4 to i. ibecoms an int. On the next line iis being auto-boxed to its corresponding ReferenceType (class), so that the warning is obvious. We could even write
var b = i is int;
I hope this can contribute to some kind of clearification on this topic.