0

I am pulling data from a public api which I do not control.

One object contains a property "space":40110198681182961664. This property is too large for an ulong.

The correct value type would be BigInteger. And here comes the issue. System.Text.Json can only serialize BigIntegers to and from string, eg: "space":"40110198681182961664"

I have tried other options such as serializing to string but system.text.json will not serialize a numeric value to string.

What options do I have to serialize/deserialize this value with system.text.json?

julian bechtold
  • 1,875
  • 2
  • 19
  • 49
  • Have you thought of implementing your own custom serializer for this one object? Inherit from [JsonConverter](https://learn.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonconverter-1?view=net-7.0) and if the type being serialized is your object type, customize the serialization to and from string? Also found this on [SO](https://stackoverflow.com/questions/58074304/is-polymorphic-deserialization-possible-in-system-text-json), see the answer. – Simon Wilson Mar 02 '23 at 15:56
  • There's a `BigIntegerConverter` in [this answer](https://stackoverflow.com/a/65350863/3744182) to [Serialising BigInteger using System.Text.Json](https://stackoverflow.com/q/64788895/3744182). – dbc Mar 02 '23 at 16:28
  • Depending on what you need to do, you can do `JsonSerializer.Deserialize(jsonString);`. It will deserialize to [ValueKind](https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonelement.valuekind?view=net-7.0) of number. That said, a custom `JsonConverter` seems more efficient. – thewallrus Mar 02 '23 at 16:30

1 Answers1

-1

Implementing a custom value Converter

First, add a class which is capable to read the raw value and convert to the target type:

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

public class BigIntegerConverter : JsonConverter<BigInteger>
{
    /// <summary>
    /// Converts a JSON value to a <see cref="BigInteger"/>.
    /// </summary>
    /// <param name="reader">The <see cref="Utf8JsonReader"/> to read from.</param>
    /// <param name="typeToConvert">The type of the object to convert.</param>
    /// <param name="options">The serializer options to use.</param>
    /// <returns>The converted <see cref="BigInteger"/>.</returns>
    public override BigInteger Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        // Check if the input is a numeric value
        if (reader.TokenType == JsonTokenType.Number)
        {
            // Try to parse the input directly as a BigInteger using Utf8Parser
            ReadOnlySpan<byte> span = reader.ValueSpan;
            string stringValue = Encoding.UTF8.GetString(span);
            if (BigInteger.TryParse(stringValue, out BigInteger result))
            {
                return result;
            }
        }
        // Check if the input is a string value
        else if (reader.TokenType == JsonTokenType.String)
        {
            // Try to parse the input as a BigInteger using BigInteger.TryParse
            if (BigInteger.TryParse(reader.GetString(), out BigInteger result))
            {
                return result;
            }
        }

        // If parsing fails, throw a JsonException
        throw new JsonException($"Could not convert \"{reader.GetString()}\" to BigInteger.");
    }

    /// <summary>
    /// Writes a <see cref="BigInteger"/> value as a JSON number.
    /// </summary>
    /// <param name="writer">The <see cref="Utf8JsonWriter"/> to write to.</param>
    /// <param name="value">The value to write.</param>
    /// <param name="options">The serializer options to use.</param>
    public override void Write(Utf8JsonWriter writer, BigInteger value, JsonSerializerOptions options)
    {
        // Convert the BigInteger value to a byte array using UTF8 encoding
        byte[] bytes = Encoding.UTF8.GetBytes(value.ToString());

        // Write the byte array as a raw JSON numeric value (without quotes)
        writer.WriteRawValue(Encoding.UTF8.GetString(bytes));
    }
}

next add the custom converter to your JsonSerializerOptions:

var options = new JsonSerializerOptions();
    options.Converters.Add(new BigIntegerConverter());
return JsonSerializer.Deserialize<T>(inputString);

Last, you need to mark your variable in the class which should be serialized/deserialized:

[JsonConverter(typeof(BigIntegerConverter))] //<<-
public BigInteger space { get; set; }
julian bechtold
  • 1,875
  • 2
  • 19
  • 49
  • I see it took less then 10 mins to prepare the answer. I am sure you spent much more time preparing the question – Serge Mar 02 '23 at 17:01
  • 1
    You have to include your answer in your question and explain that you are looking for the better implementation. And I am amazed that your question was not closed as a duplicate, since there are several BigInt quesitons and answers. – Serge Mar 02 '23 at 18:50
  • 1
    *Any improvements or better answers are welcomed* -- you should parse and format in the invariant locale `CultureInfo.InvariantCulture`. As it is your JSON parsing and serializing will be locale dependent, e.g. different symbols will be used for the minus sign in different locales. – dbc Mar 02 '23 at 20:00
  • 1
    Also, hypothetically, if the integer is big enough, it may be split across multiple spans and so `reader.ValueSpan` will be incomplete. In such cases you must use `reader.ValueSequence`. It's unlikely in practice though. – dbc Mar 02 '23 at 20:04