Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support System.Text.Json #1101

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions NBitcoin.Tests/AltcoinTests.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
using NBitcoin.Altcoins.Elements;
using NBitcoin.RPC;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using NBitcoin.Altcoins;
using NBitcoin.JsonConverters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Xunit;
using Encoders = NBitcoin.DataEncoders.Encoders;
Expand Down
15 changes: 12 additions & 3 deletions NBitcoin/NBitcoin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,25 @@
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' ">
<DefineConstants>$(DefineConstants);NO_RCA;NOPARALLEL;NETSTANDARD;NETSTANDARD1X;NULLABLE_SHIMS;NO_MEM_BUFFER;NOTRACESOURCE;NOCUSTOMSSLVALIDATION;NOSTRNORMALIZE;NOSOCKET;NOFILEIO;USEBC;NODEFAULTRNG;NODYNAMIC;NOX509;NONATIVEHASH;NO_ARRAY_FILL;NO_NATIVE_HMACSHA512;NO_THREAD;NO_NATIVERIPEMD160;NO_NATIVESHA1;NO_SOCKETASYNC</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DefineConstants>$(DefineConstants);SECP256K1_VERIFY</DefineConstants>
<DefineConstants>NOJSONNET</DefineConstants>
<NOJSONNET>true</NOJSONNET>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="System.Buffers" Version="4.5.0" Condition="'$(TargetFramework)' != 'netstandard2.1' And '$(TargetFramework)' != 'net6.0'" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" Condition="!$(NOJSONNET)" />
<PackageReference Include="System.Text.Json" Version="6.0.4" Condition="$(NOJSONNET)" />

<PackageReference Include="System.Buffers" Version="4.5.1" Condition="'$(TargetFramework)' != 'netstandard2.1' And '$(TargetFramework)' != 'net6.0'" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.0.0" />
</ItemGroup>
<ItemGroup Condition="$(NOJSONNET)">
<Content Remove="JsonConverters\**\*" />
<Compile Remove="JsonConverters\**\*" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<Reference Include="System.Net.Http" />

</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.Collections.NonGeneric" Version="4.3.0" />
Expand Down
9 changes: 1 addition & 8 deletions NBitcoin/RPC/FundRawTransactionOptions.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NBitcoin.RPC
namespace NBitcoin.RPC
{
public class FundRawTransactionOptions
{
Expand Down
83 changes: 71 additions & 12 deletions NBitcoin/RPC/ImportMultiAddress.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

#if !NOJSONNET
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NBitcoin.JsonConverters;
#else
using System.Text.Json;
using System.Text.Json.Serialization;
using NBitcoin.SystemJsonConverters;
#endif
using NBitcoin.Scripting;

namespace NBitcoin.RPC
{

[JsonObject(MemberSerialization.OptIn)]
#if !NOJSONNET
[JsonElement(MemberSerialization.OptIn)]
#else
[JsonConverter(typeof(MemberOptInJsonConverter))]
#endif
public class ImportMultiAddress
{
public class ScriptPubKeyObject
Expand All @@ -27,7 +41,12 @@ public ScriptPubKeyObject(BitcoinAddress address)
[JsonIgnore]
public Script ScriptPubKey { get; set; }

#if !NOJSONNET
[JsonProperty("address", NullValueHandling = NullValueHandling.Ignore)]
#else
[JsonPropertyName("address")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
#endif
public BitcoinAddress Address { get; set; }

/// <summary>
Expand All @@ -43,39 +62,79 @@ public bool IsAddress
}
}
}

#if !NOJSONNET
[JsonProperty("scriptPubKey", NullValueHandling = NullValueHandling.Ignore)]
#else
[JsonPropertyName("scriptPubKey")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
#endif
[JsonConverter(typeof(ImportMultiScriptPubKeyConverter))]
public ScriptPubKeyObject ScriptPubKey { get; set; }

/// <summary>
/// Creation time of the key, keep null if this address has just been generated
/// </summary>
#if !NOJSONNET
[JsonProperty("timestamp")]
#else
[JsonPropertyName("timestamp")]
#endif
public DateTimeOffset? Timestamp { get; set; }

#if !NOJSONNET
[JsonProperty("redeemscript", NullValueHandling = NullValueHandling.Ignore)]
#else
[JsonPropertyName("redeemscript")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
#endif
public Script RedeemScript { get; set; }

#if !NOJSONNET
[JsonProperty("pubkeys", NullValueHandling = NullValueHandling.Ignore)]
#else
[JsonPropertyName("pubkeys")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
#endif
public PubKey[] PubKeys { get; set; }

#if !NOJSONNET
[JsonProperty("keys", NullValueHandling = NullValueHandling.Ignore)]
#else
[JsonPropertyName("keys")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
#endif
public BitcoinSecret[] Keys { get; set; }

#if !NOJSONNET
[JsonProperty("internal", NullValueHandling = NullValueHandling.Ignore)]
#else
[JsonPropertyName("internal")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
#endif
public bool? Internal { get; set; }

#if !NOJSONNET
[JsonProperty("watchonly", NullValueHandling = NullValueHandling.Ignore)]
#else
[JsonPropertyName("watchonly")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
#endif
public bool? WatchOnly { get; set; }

#if !NOJSONNET
[JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
#else
[JsonPropertyName("label")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
#endif
public string Label { get; set; }

#if !NOJSONNET
[JsonProperty("desc", NullValueHandling = NullValueHandling.Ignore)]
#else
[JsonPropertyName("desc")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
#endif
public OutputDescriptor Desc { get; set; }

#if !NOJSONNET
[JsonProperty("range", NullValueHandling = NullValueHandling.Ignore)]
#else
[JsonPropertyName("range")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
#endif
public int[] Ranges { get; set; }

[JsonIgnore]
Expand Down
7 changes: 1 addition & 6 deletions NBitcoin/RPC/SignRawTransactionWithKeyRequest.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;

namespace NBitcoin.RPC
namespace NBitcoin.RPC
{
public class SignRawTransactionRequest
{
Expand Down
93 changes: 93 additions & 0 deletions NBitcoin/SystemJsonConverters/MemberOptInJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace NBitcoin.SystemJsonConverters
{
public class MemberOptInJsonConverter<T> : JsonConverter<T> where T : class
{
public override bool HandleNull => false;

public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return JsonSerializer.Deserialize<T>(ref reader, options);
}

public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
if (value is null) return;
writer.WriteStartObject();
var type = value.GetType();
var props = type.GetProperties();

foreach (var property in props)
{
var propValue = property.GetValue(value);
var jsonAttribute = property.GetCustomAttribute<JsonPropertyNameAttribute>();
if (jsonAttribute is null)
{
continue;
}

var ignoreAttribute = property.GetCustomAttribute<JsonIgnoreAttribute>();
if (ignoreAttribute is not null)
{
switch (ignoreAttribute.Condition)
{
case JsonIgnoreCondition.Never:
break;
case JsonIgnoreCondition.Always:
continue;
case JsonIgnoreCondition.WhenWritingDefault:
if (propValue == null)
{
continue;
}

break;
case JsonIgnoreCondition.WhenWritingNull:

if (propValue == default)
{
continue;
}

break;
default:
throw new ArgumentOutOfRangeException();
}
}

switch (true)
{
case true when propValue is not null:
case true when propValue is null && options.DefaultIgnoreCondition == JsonIgnoreCondition.Never:
writer.WritePropertyName(property.Name);
JsonSerializer.Serialize(writer, propValue, options);
break;
}
}

writer.WriteEndObject();
}
}

public class MemberOptInJsonConverter : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) => !typeToConvert.IsPrimitive;

public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
JsonConverter converter = (JsonConverter) Activator.CreateInstance(
typeof(MemberOptInJsonConverter<>)
.MakeGenericType(new Type[] {typeToConvert}),
BindingFlags.Instance | BindingFlags.Public,
binder: null,
args: null,
culture: null)!;

return converter;
}
}
}