ic4j / ic4j-candid

Java Candid for The Internet Computer (IC4J) is a set of native Java libraries to manage the Internet Computer Candid types
Apache License 2.0
3 stars 2 forks source link

NullPointerException while trying to deserialize byte array of respose from canister. #1

Closed chinaryov closed 2 years ago

chinaryov commented 2 years ago

I am trying to load data from canister and deserialize it.

The error is:

Exception in thread "main" java.lang.NullPointerException
    at org.ic4j.candid.Deserializer.deserializeRecord(Deserializer.java:399)
    at org.ic4j.candid.Deserializer.deserializeAny(Deserializer.java:110)
    at org.ic4j.candid.Deserializer.deserializeRecord(Deserializer.java:424)
    at org.ic4j.candid.Deserializer.deserializeAny(Deserializer.java:110)
    at org.ic4j.candid.Deserializer.deserializeVec(Deserializer.java:325)
    at org.ic4j.candid.Deserializer.deserializeAny(Deserializer.java:106)
    at org.ic4j.candid.parser.IDLValue.deserialize(IDLValue.java:201)
    at org.ic4j.candid.IDLDeserialize.getValue(IDLDeserialize.java:56)
    at org.ic4j.candid.parser.IDLArgs.fromBytes(IDLArgs.java:56)

To reproduce: canister url: https://pk6rk-6aaaa-aaaae-qaazq-cai.raw.ic0.app/ query method: "listings"

Same canister and "getRegistry" query method works without any problem.

rdobrik commented 2 years ago

Hi Alexander,

Thanks for reporting. I will reproduce the problem and try to figure out what is going wrong. At least we need to handle this exception properly with proper error message. Do you have candid structure you are trying to deserialize? In text form?

Roman

Sent from my iPhone

On Feb 10, 2022, at 3:12 AM, Alexander Chinaryov @.***> wrote:

 I am trying to load data from canister and deserialize it.

The error is:

Exception in thread "main" java.lang.NullPointerException at org.ic4j.candid.Deserializer.deserializeRecord(Deserializer.java:399) at org.ic4j.candid.Deserializer.deserializeAny(Deserializer.java:110) at org.ic4j.candid.Deserializer.deserializeRecord(Deserializer.java:424) at org.ic4j.candid.Deserializer.deserializeAny(Deserializer.java:110) at org.ic4j.candid.Deserializer.deserializeVec(Deserializer.java:325) at org.ic4j.candid.Deserializer.deserializeAny(Deserializer.java:106) at org.ic4j.candid.parser.IDLValue.deserialize(IDLValue.java:201) at org.ic4j.candid.IDLDeserialize.getValue(IDLDeserialize.java:56) at org.ic4j.candid.parser.IDLArgs.fromBytes(IDLArgs.java:56) To reproduce: canister url: https://pk6rk-6aaaa-aaaae-qaazq-cai.raw.ic0.app/ query method: "listings"

Same canister and "getRegistry" query method works without any problem.

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you are subscribed to this thread.

rdobrik commented 2 years ago

Hi Alexander,

Can you share your canister id?

Thanks,

Roman

On Thu, Feb 10, 2022 at 3:12 AM Alexander Chinaryov < @.***> wrote:

I am trying to load data from canister and deserialize it.

The error is:

Exception in thread "main" java.lang.NullPointerException at org.ic4j.candid.Deserializer.deserializeRecord(Deserializer.java:399) at org.ic4j.candid.Deserializer.deserializeAny(Deserializer.java:110) at org.ic4j.candid.Deserializer.deserializeRecord(Deserializer.java:424) at org.ic4j.candid.Deserializer.deserializeAny(Deserializer.java:110) at org.ic4j.candid.Deserializer.deserializeVec(Deserializer.java:325) at org.ic4j.candid.Deserializer.deserializeAny(Deserializer.java:106) at org.ic4j.candid.parser.IDLValue.deserialize(IDLValue.java:201) at org.ic4j.candid.IDLDeserialize.getValue(IDLDeserialize.java:56) at org.ic4j.candid.parser.IDLArgs.fromBytes(IDLArgs.java:56)

To reproduce: canister url: https://pk6rk-6aaaa-aaaae-qaazq-cai.raw.ic0.app/ query method: "listings"

Same canister and "getRegistry" query method works without any problem.

— Reply to this email directly, view it on GitHub https://github.com/ic4j/ic4j-candid/issues/1, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACDHPX6SF6DTGJAKGVVJRL3U2OMTDANCNFSM5OALXM3Q . You are receiving this because you are subscribed to this thread.Message ID: @.***>

chinaryov commented 2 years ago

Hello Roman,

Canister ID is pk6rk-6aaaa-aaaae-qaazq-cai More information you can find here: https://ic.rocks/principal/pk6rk-6aaaa-aaaae-qaazq-cai Query method "listings". There also available candid structure and binary data as a response of query method "listings"

Best regards, Alexander

rdobrik commented 2 years ago

Hi Alexander,

I was able to find and fix the bug (actually discovered another one behind). Let me run some unit tests, so I am not going to break something else and I can push it to Github and push a new Release Candidate to Maven Central.This was a very good use case, with nested Vector, Record, Variant and Optional types. Please let me know if you have any other issues, really appreciate your help, especially Candid complex types that have myriads of variations, I need to be sure we handle all of them properly.

Also, you are using unnamed Record elements, I need to figure out how to map it into native Java objects. For now the IDLValue type works fine. I will send you a sample with IDLType template, so hashes will be mapped to real string names in IDLValue HashMap. In the future I would like to also develop Candid Java parser to read Candid IDL files and create IDLType object (in the runtime) or generate Java Pojo classes and service Interface.

I will let you know when the new release is ready.

Roman

On Thu, Feb 10, 2022 at 11:45 AM Alexander Chinaryov < @.***> wrote:

Hello Roma,

Canister ID is pk6rk-6aaaa-aaaae-qaazq-cai More information you can find here: https://ic.rocks/principal/pk6rk-6aaaa-aaaae-qaazq-cai Query method "listings". There also available candid structure and binary data as a response of query method "listings"

Best regards, Alexander

— Reply to this email directly, view it on GitHub https://github.com/ic4j/ic4j-candid/issues/1#issuecomment-1035413238, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACDHPXY3EVPYAZKGH76G6L3U2QIVZANCNFSM5OALXM3Q . You are receiving this because you commented.Message ID: @.***>

chinaryov commented 2 years ago

Hi Roman,

Thank you for your fast fix. I just checked a new version and it works good!

Best regards, Alexander

rdobrik commented 2 years ago

Hi Alexander,

Happy to see that the fix helped to solve your problem. As I promised , I defined IDLType structure for your listings type, so hashes in response will map to real names. Here the code you can use , sure you can change variable names:

Map<Label,IDLType> rootRecord = new TreeMap<Label,IDLType>(); rootRecord.put(Label.createUnnamedLabel(0l), IDLType.createType(Type.NAT32));

Map<Label,IDLType> offerRecord = new TreeMap<Label,IDLType>(); offerRecord.put(Label.createNamedLabel("locked"), IDLType.createType(Type.OPT)); offerRecord.put(Label.createNamedLabel("seller"), IDLType.createType(Type.PRINCIPAL)); offerRecord.put(Label.createNamedLabel("price"), IDLType.createType(Type.NAT64));

rootRecord.put(Label.createUnnamedLabel(1l), IDLType.createType(Type.RECORD, offerRecord));

Map<Label,IDLType> typeVariant = new TreeMap<Label,IDLType>();

Map<Label,IDLType> nonfungibleRecord = new TreeMap<Label,IDLType>(); nonfungibleRecord.put(Label.createNamedLabel("metadata"), IDLType.createType(Type.OPT));

typeVariant.put(Label.createNamedLabel("nonfungible"), IDLType.createType(Type.RECORD,nonfungibleRecord));

rootRecord.put(Label.createUnnamedLabel(2l), IDLType.createType(Type.VARIANT, typeVariant));

IDLType idlType = IDLType.createType(Type.VEC, IDLType.createType(Type.RECORD, rootRecord)); IDLType[] idlTypes = { idlType };

then just use idlTypes variable as a second parameter for deserializer:

byte[] queryOutput = queryResponse.get();

IDLArgs outArgs = IDLArgs.fromBytes(queryOutput, idlTypes);

Of course, it will be better to map it to real Java classes. I just added functionality to our PojoDeserializer (and PojoSerializer as well) to support unnamed RECORD elements and provide also support for VARIANT Pojo ser/deser. This functionality will come in the next Release Candidate, probably in a few days.

Pojo class with annotation (it's just a root class root array element)

import org.ic4j.candid.annotations.Field; import org.ic4j.candid.annotations.Id; import org.ic4j.candid.types.Type;

public class TradingListing { @Id(0) @Field(Type.NAT32) public Integer id;

@Id(1) @Field(Type.RECORD)
public TradingOffer offer;

@Id(2) @Field(Type.VARIANT)
public TradingOfferType offerType;
}

then use dynamic proxy Builder or just simply deserialize byte array response

TradingListing[] listingsResult = IDLArgs.fromBytes(queryOutput).getArgs().get(0).getValue(new PojoDeserializer(), TradingListing[].class);

I will let you know, when the new release is out. Then also share Pojo java classes.

Roman

rdobrik commented 2 years ago

Hi Alexander,

New Candidate Release 6 is out. Significantly improved support for complex candid types, thanks to your use case! Very interesting app you are working on, we are happy to help. I included your listings data types to our acceptance unit test. Now we support those types in our POJO, JSON (Jackson, GSON and XML (DOM) serializers and deserializers. Also added unit tests for those.

Let me know, if you need some additional help or you are facing any issues related to Java Agent.

Roman

On Fri, Feb 11, 2022 at 12:11 AM Alexander Chinaryov < @.***> wrote:

Closed #1 https://github.com/ic4j/ic4j-candid/issues/1.

— Reply to this email directly, view it on GitHub https://github.com/ic4j/ic4j-candid/issues/1#event-6049298670, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACDHPX2OV6Y5646WYN4HSZLU2TACXANCNFSM5OALXM3Q . You are receiving this because you commented.Message ID: @.***>