0
votes

TL; DR;

I need Corda to serialize JSONObject from JDK. I suspect I can add a custom parser for it, but I can't figure out how.

Full story:

So I have a bunch of classes that act as semy-type safe wrappers around a JSON api. An example:

@CordaSerializable
class ClaimReq(val json: JSONObject) {
    val proverDid: String = json.getString("prover_did")
    val credDefId: String = json.getString("cred_def_id")
}

The problem appeared when I tried to send such a class through a Corda channel. The system had a problem serializing the org.json.JSONObject:

java.io.NotSerializableException: No constructor for deserialization found for class org.json.JSONObject. at net.corda.nodeapi.internal.serialization.amqp.SerializationHelperKt.constructorForDeserialization(SerializationHelper.kt:50) ~[corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SchemaKt.fingerprintForObject(Schema.kt:456) ~[corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SchemaKt.access$fingerprintForObject(Schema.kt:1) ~[corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SchemaKt$fingerprintForType$3.invoke(Schema.kt:423) ~[corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SchemaKt$fingerprintForType$3.invoke(Schema.kt) ~[corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SchemaKt.fingerprintWithCustomSerializerOrElse(Schema.kt:345) ~[corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SchemaKt.fingerprintForType(Schema.kt:417) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SchemaKt.fingerprintForObject(Schema.kt:459) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SchemaKt.access$fingerprintForObject(Schema.kt:1) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SchemaKt$fingerprintForType$3.invoke(Schema.kt:423) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SchemaKt$fingerprintForType$3.invoke(Schema.kt) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SchemaKt.fingerprintWithCustomSerializerOrElse(Schema.kt:345) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SchemaKt.fingerprintForType(Schema.kt:417) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SchemaKt.fingerprintForType$default(Schema.kt:352) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SchemaKt.fingerprintForType(Schema.kt:328) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.ObjectSerializer.(ObjectSerializer.kt:34) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SerializerFactory$makeClassSerializer$1.apply(SerializerFactory.kt:271) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SerializerFactory$makeClassSerializer$1.apply(SerializerFactory.kt:40) [corda-node-api-3.1-corda.jar:?] at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1688) [?:1.8.0_171] at net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.makeClassSerializer(SerializerFactory.kt:255) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.get(SerializerFactory.kt:100) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SerializationOutput.writeObject$node_api(SerializationOutput.kt:98) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SerializationOutput.writeObject$node_api$default(SerializationOutput.kt:97) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SerializationOutput.writeObject$node_api(SerializationOutput.kt:78) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SerializationOutput$_serialize$1$1.invoke(SerializationOutput.kt:64) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SerializationOutput$_serialize$1$1.invoke(SerializationOutput.kt:22) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SerializationHelperKt.withList(SerializationHelper.kt:401) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SerializationOutput$_serialize$1.invoke(SerializationOutput.kt:63) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SerializationOutput$_serialize$1.invoke(SerializationOutput.kt:22) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SerializationHelperKt.withDescribed(SerializationHelper.kt:390) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SerializationOutput._serialize$node_api(SerializationOutput.kt:62) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.SerializationOutput.serialize(SerializationOutput.kt:36) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.amqp.AbstractAMQPSerializationScheme.serialize(AMQPSerializationScheme.kt:128) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.SerializationFactoryImpl$serialize$1$1.invoke(SerializationScheme.kt:126) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.SerializationFactoryImpl$serialize$1$1.invoke(SerializationScheme.kt:86) [corda-node-api-3.1-corda.jar:?] at net.corda.core.serialization.SerializationFactory.withCurrentContext(SerializationAPI.kt:66) [corda-core-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.SerializationFactoryImpl$serialize$1.invoke(SerializationScheme.kt:126) [corda-node-api-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.SerializationFactoryImpl$serialize$1.invoke(SerializationScheme.kt:86) [corda-node-api-3.1-corda.jar:?] at net.corda.core.serialization.SerializationFactory.asCurrent(SerializationAPI.kt:80) [corda-core-3.1-corda.jar:?] at net.corda.nodeapi.internal.serialization.SerializationFactoryImpl.serialize(SerializationScheme.kt:126) [corda-node-api-3.1-corda.jar:?] at net.corda.core.serialization.SerializationAPIKt.serialize(SerializationAPI.kt:221) [corda-core-3.1-corda.jar:?] at net.corda.core.serialization.SerializationAPIKt.serialize$default(SerializationAPI.kt:220) [corda-core-3.1-corda.jar:?] at net.corda.node.services.statemachine.FlowStateMachineImpl.createSessionData(FlowStateMachineImpl.kt:353) [corda-node-3.1-corda.jar:?] at net.corda.node.services.statemachine.FlowStateMachineImpl.send(FlowStateMachineImpl.kt:235) [corda-node-3.1-corda.jar:?] at net.corda.node.services.statemachine.FlowSessionImpl.send(FlowSessionImpl.kt:52) [corda-node-3.1-corda.jar:?] at net.corda.node.services.statemachine.FlowSessionImpl.send(FlowSessionImpl.kt:56) [corda-node-3.1-corda.jar:?] at com.luxoft.blockchainlab.corda.hyperledger.indy.flow.IssueClaimFlow$Prover.call(IssueClaimFlow.kt:85) [main/:?] at com.luxoft.blockchainlab.corda.hyperledger.indy.flow.IssueClaimFlow$Prover.call(IssueClaimFlow.kt:71) [main/:?] at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:96) [corda-node-3.1-corda.jar:?] at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:44) [corda-node-3.1-corda.jar:?] at co.paralleluniverse.fibers.Fiber.run1(Fiber.java:1092) [quasar-core-0.7.9-jdk8.jar:0.7.9] at co.paralleluniverse.fibers.Fiber.exec(Fiber.java:788) [quasar-core-0.7.9-jdk8.jar:0.7.9]

So I fixed the problem with a scpecific constructor and a getter:

@CordaSerializable
class ClaimReq(val json: JSONObject) {
    val proverDid: String = json.getString("prover_did")
    val credDefId: String = json.getString("cred_def_id")

    @ConstructorForDeserialization constructor(str: String) : this(JSONObject(str))
    val str = json.toString()
}

Unfortunately that solution requires to add 2 lines to every class declaration. Is there any way I could abstract it out, like in a super-class? Or maybe I could extend Corda serialization with a custom parcer to support JSONObject?

1

1 Answers

1
votes

Any classes serialised by the Corda framework that have more than one constructor require one of the constructors to be annotated with @ConstructorForDeserialization. Obviously, there is no constructor with this annotation in JSONObject.

You need to provide a custom serializer for the JSONObject class. See an example of defining a custom serializer here: https://docs.corda.net/head/cordapp-custom-serializers.html#example.

If this serializer is on the classpath, it will be used by the node at runtime.