JamesNK / Newtonsoft.Json

Json.NET is a popular high-performance JSON framework for .NET
https://www.newtonsoft.com/json
MIT License
10.71k stars 3.24k forks source link

JsonReader.ReadAsString() doesn't return the contents of JsonToken.Raw tokens #2876

Closed mlorbetske closed 1 week ago

mlorbetske commented 1 year ago

Source/destination types and Repro Steps

[JsonConverter(typeof(DecimalLikeConverter))]
public class DecimalLikeThing
{
    public decimal D { get; set; }
}

public class DecimalLikeConverter : JsonConverter
{
    public override bool CanConvert(Type objectType) => true;

    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
    {
        reader.Read();
        var value = reader.ReadAsString();
        var result = new DecimalLikeThing
        {
            D = decimal.Parse(value ?? "0"),
        };

        reader.Read();
        return result;
    }

    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
    {
        var x = (DecimalLikeThing)value!;
        writer.WriteStartObject();
        writer.WritePropertyName("D");
        writer.WriteRawValue(x.D.ToString());
        writer.WriteEndObject();
    }
}

public void Run()
{
    var x = new DecimalLikeThing
    {
       D = 100.300M,
    };

    var res = JToken.FromObject(x);
    res.ToObject<DecimalLikeThing>(); //<-- This throws the exception noted below

    var text = res.ToString(Formatting.None);
    var text = res.ToString(Formatting.None);
    var f = JsonConvert.DeserializeObject(text, typeof(DecimalLikeThing)); // <-- This succeeds because the JToken doesn't contain a token noted to by of type Raw
}

Source/destination JSON

{
  "D": 100.300
}

Expected behavior

The ReadAsString() call in the JsonConverter above should have returned the text captured in the Raw type token

Actual behavior

Newtonsoft.Json.JsonReaderException: 'Error reading string. Unexpected token: Raw. Path 'D'.' at Newtonsoft.Json.JsonReader.ReadAsString() in /_/Src/Newtonsoft.Json/JsonReader.cs:line 507

mlorbetske commented 1 year ago

PR available in https://github.com/JamesNK/Newtonsoft.Json/pull/2877

elgonzo commented 1 year ago

It's debatable whether this is a bug requiring a fix or whether this is intentional behavior, and since i am not the author nor a maintainer of Newtonsoft.Json i leave it to them to evaluate this situation.

However, as a side note and unrelated to the issue your report is about, please note that doing writer.WriteRawValue(x.D.ToString()); in your WriteJson method is wrong, as it can lead to invalid json. Transforming the decimal to a string using the parameterless ToString() method uses the current culture. If the current culture uses a decimal comma, writing x.D.ToString() as raw value will screw up the resulting json.

mlorbetske commented 1 year ago

@elgonzo thanks for the response. yep, the converter was just an example, not intended to be production-ready code.