MetacoSA / NBitpayClient

A non official library for using bitpay's API
MIT License
19 stars 18 forks source link

Invoice JSON deserialization error #31

Open gradientskier opened 4 years ago

gradientskier commented 4 years ago

Dear BTCPay team,

I am using NBitpayClient 1.0.0.39:

<PackageReference Include="NBitpayClient" Version="1.0.0.39" />

I am trying to serialize and deserialize in JSON the invoice structure using System.Text.Json, where MyBTCPay is an instance of NBitpayClient.Bitpay:

using System.Text.Json;

var satsAmount = 100000;
var invoice = await MyBTCPay.CreateInvoiceAsync(new NBitpayClient.Invoice(satsAmount, "SATS"));
var jsonString = JsonSerializer.Serialize(invoice);
var invoice2 = JsonSerializer.Deserialize<NBitpayClient.Invoice>(jsonString);

But when I try to deserialize the jsonString into invoice2, I get the following error:

---> System.NotSupportedException: Deserialization of reference types without parameterless constructor is not supported. Type 'NBitcoin.Money'

Do you have any suggestion about how I can properly serialize/deserialize a NBitpayClinet Invoice in JSON?

Many thanks in advance!

gradientskier commented 4 years ago

Serialization and deserialization works with the Newtonsoft JSON serializer/deserializer. I saw that it is the one used in the library.

using Newtonsoft.Json;
[...]
var jsonString = JsonConvert.SerializeObject(invoice);
var invoice2 = JsonConvert.DeserializeObject<NBitpayClient.Invoice>(jsonString);
NicolasDorier commented 4 years ago

Because there is some non primitive type in the object, you need the proper json converters exposed by NBitcoin.JsonConverters.Serializer.

var str = NBitcoin.JsonConverters.Serializer.ToString(invoice);

You can probably make a PR so your original code work by adding [JsonConverter] attribute on top of the custom types.

gradientskier commented 4 years ago

As you said, this is an issue related to the way I was trying serialization and deserialization and not an issue of the repo.

The missing feature in this repo is that System.Text.Json serialization and deserialization is not supported. Are you referring to a pull request that would support System.Text.Json serializer/deserializer for the Invoice, in addition to the Newtonsoft one?

BTW, just for information that may be useful to other developers on top of BTCPay that want to cache the invoice in their applications, this is how I serialized/deserialized a BTCPay Invoice into/from a text field in a database with EF Core; I followed a suggestion from here: https://stackoverflow.com/questions/44829824/how-to-store-json-in-an-entity-field-with-ef-core

In my MyCustomModel

public class MyCustomModel
{
    public Invoice? BTCPayInvoice {get; set;}
}

In my ApplicationDbContext

// This Converter will perform the conversion to and from Json to the desired type
modelBuilder.Entity<MyCustomModel>().Property(e => e.BTCPayInvoice).HasConversion(
    convertToProviderExpression: (v) => Newtonsoft.Json.JsonConvert.SerializeObject(v),
    convertFromProviderExpression: (v) => Newtonsoft.Json.JsonConvert.DeserializeObject<NBitpayClient.Invoice>(v));

Now there are only problems related to the fact that a lot of the Invoice fields (e.g. PaymentCodes) are not actually serialized on purpose. Maybe extending from the Invoice and overriding the relevant ShouldSerialize methods could solve this.

NicolasDorier commented 4 years ago

@gradientskier did not noticed you were using System.Text.Json. Yes, we are using newtonsoft.

To be honest, the type here is weird. (see the code, not all fields are serialized as you saw) I forked this project from Bitpay source code, with little change to the entities.