IC4J API Docs
  • Overview
    • Introduction
  • Quick Start
  • License
  • Reference
    • API Reference
      • Install IC4J Libraries
      • Supported Types
      • ReplicaTransport
      • Identity
      • Principal
      • AgentBuilder
      • ProxyBuilder
      • Using IDLArgs
      • QueryBuilder and UpdateBuilder
      • Using Raw Agent Methods
      • Handle Binary Payloads
      • Object Serializers and Deserializers
        • Pojo Serializer and Deserializer
        • JSON Jackson Serializer and Deserializer
        • JSON Gson Serializer and Deserializer
        • XML DOM Serializer and Deserializer
        • XML JAXB Serializer and Deserializer
        • JDBC Serializer
      • Android Development
Powered by GitBook
On this page
  1. Reference
  2. API Reference
  3. Object Serializers and Deserializers

Pojo Serializer and Deserializer

PreviousObject Serializers and DeserializersNextJSON Jackson Serializer and Deserializer

Last updated 1 year ago

Use and to serialize and deserialize the annotated POJO (Plain Old Java Object) to and from Candid payload of type RECORD.

Serializer and Deserializer will use Candid Java annotations to get the Candid Name and Type.

A fully functional example using and can be found .

This example calls a canister using Motoko .

The canister uses 2 complex types, LoanApplication and LoanOffer.

 // Loan Application
public type LoanApplication = {
    id: Nat;
    firstname: Text;
    lastname: Text;
    zipcode: Text;
    ssn: Text;
    amount: Float;
    term: Nat16;
    created: Int;
 };

// Loan Offer
public type LoanOffer = {
    providerid: Principal;
    providername: Text;
    applicationid: Nat;
    apr: Float;
    created: Int;
};

The canister has 2 methods apply and getOffers.

 public shared (msg) func apply(input : LoanApplication) : async LoanOffer { 
       counter += 1;
        Debug.print("Loan Application for user " #Principal.toText(msg.caller));
        
        let offer  : LoanOffer = {
            providerid = Principal.fromActor(this);
            providername = "Loan Provider";
            applicationid = counter;
            apr = 3.14;
            created = Time.now();
        };

        var userOffers :  ?Offers<LoanOffer> = offers.get(msg.caller);

        switch userOffers {
            case (null) { var userOffer : Offers<LoanOffer> = Buffer.Buffer(0); userOffer.add(offer);  offers.put(msg.caller, userOffer)};
            case (?userOffer) { userOffer.add(offer); };
        };
        return offer;
};

public query (msg) func getOffers() : async [LoanOffer] {
        var userOffers :  ?Offers<LoanOffer> = offers.get(msg.caller);

        switch userOffers {
            case (null) { return [] };
            case (?userOffer) { return userOffer.toArray() };
        };
};
LoanProxy.java
public interface LoanProxy {
	@UPDATE
	@Name("apply")
	@Waiter(timeout = 30)
	@ResponseClass(LoanOffer.class)
	public CompletableFuture<LoanOffer> apply(@Argument(Type.RECORD) LoanApplication loanApplication);
	
	@QUERY
	@Name("getOffers")
	public LoanOffer[] getOffers();
}
LoanApplication.java
public class LoanApplication{
    @Field(Type.NAT)
    public BigInteger id;
    public Double amount;
    @Field(Type.NAT16)    
    public Short term;
    @Name("firstname")
    public String firstName;
    @Name("lastname")
    public String lastName;
    public String ssn;
    public String zipcode;
    public BigInteger created;
}
LoanOffer.java
public class LoanOffer{
    @Field(Type.PRINCIPAL)
    @Name("providerid")
    public String providerId;	
    @Name("providername")
    public String providerName;    
    @Field(Type.PRINCIPAL)
    @Name("userid")
    public Principal userId;
    @Field(Type.NAT)
    @Name("applicationid")
    public Integer applicationId;
    public Double apr;
    @Field(Type.INT)
    public Long created;
}
Main.java
LoanApplication loanApplication = new LoanApplication();
loanApplication.firstName = "John";
loanApplication.lastName = "Doe";
loanApplication.ssn = "111-11-1111";
loanApplication.term = 48;
loanApplication.zipcode = "95134";		
loanApplication.amount = (double) 20000.00;
loanApplication.id = new BigInteger("11");
loanApplication.created = new BigInteger("0");
		
LoanProxy loanProxy = ProxyBuilder
		.create(agent, Principal.fromString(icCanister))
		.getProxy(LoanProxy.class);
		
CompletableFuture<LoanOffer> response = loanProxy.apply(loanApplication);		
LoanOffer loanOffer = response.get();				
LOG.info("Loan Offer APR is " + loanOffer.apr);		
Main.java
IDLValue idlValue = IDLValue.create(loanApplication, new PojoSerializer());
List<IDLValue> args = new ArrayList<IDLValue>();
args.add(idlValue);
IDLArgs idlArgs = IDLArgs.create(args);

byte[] payload = idlArgs.toBytes();

CompletableFuture<byte[]> response = UpdateBuilder
	.create(agent, Principal.fromString(canisterid), "apply")
	.expireAfter(Duration.ofMinutes(3))
	.arg(payload)
	.callAndWait(Waiter.create(60, 5));
	
byte[] output = queryResponse.get();

LoanOffer loanOffer = IDLArgs.fromBytes(output).getArgs().get(0).getValue(new PojoDeserializer(), LoanOffer.class);	

To call this canister the annotated proxy Java interface and will be used.

LoanApplication and LoanOffer Java classes with the Candid annotation are defined in and .

By default, PojoSerializer and PojoDeserializer will use and use the Java member variable name as the name of the Candid RECORD field.

Use Candid annotations to override the default type and to override the default name.

To skip serialization and deserialization of certain member variables, use the annotation.

will implicitly use PojoSerializer and PojoDeserializer to convert Candid RECORD to a Java object.

To use or and use PojoSerializer and PojoDeserializer directly in the Java code.

PojoSerializer
PojoDeserializer
PojoSerializer
PojoDeserializer
here
main.mo
LoanProxy.java
ProxyBuilder
LoanApplication.java
LoanOffer.java
Java to Candid type mapping
@Field
@Name
@Ignore
ProxyBuilder
Raw methods
QueryBuilder
UpdateBuilder