Runtime: deserialize object prop adds Valuekind to value

Created on 6 Nov 2019  路  7Comments  路  Source: dotnet/runtime

public class myclass {
  public object myval {get; set;}
}

function serialize() {
  var test = new myclass(){myval = 122};
  var serialized = JsonSerializer.Serialize(test);
  // "....."myval":122 ...
  var deserialized = JsonSerializer.Deserialize<myclass>(serialized);
  // deserialized.myval = "ValueKind = Number : "122""
}

This does not work very good for us. Is there a way to change this behaviour? Ideally we would like to get an in/long/double there, or just the number as a string.

area-System.Text.Json

Most helpful comment

This was a truly shocking breaking change with no documentation :(
This only cost me 4 hours of wasted time 馃憥 馃憥 馃憥 馃憥 馃憥 馃憥

All 7 comments

This seems to work, but it would be nice if it was built in:

    public class objectDeserializer : JsonConverter<object>
    {
        public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            var type = reader.TokenType;

            if (type == JsonTokenType.Number)
            {
                var oki = reader.TryGetInt32(out var vali);
                if (oki)
                {
                    return vali;
                }
                var okl = reader.TryGetInt64(out var vall);
                if (okl)
                {
                    return vall;
                }
                var okd = reader.TryGetDouble(out var val);
                if (okd)
                {
                    return val;
                }
            }

            if (type == JsonTokenType.String)
            {
                return reader.GetString();
            }

            if (type == JsonTokenType.True || type == JsonTokenType.False)
            {
                return reader.GetBoolean();
            }
            // copied from corefx repo:
            using var document = JsonDocument.ParseValue(ref reader);
            return document.RootElement.Clone();
        }

        public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
        {
            throw new NotImplementedException();
        }
    }

@maryamariyan - issue labeler flagged this incorrectly (fyi).

This does not work very good for us. Is there a way to change this behaviour? Ideally we would like to get an in/long/double there, or just the number as a string.

This is by design (and unlikely to change). Since the object type could map the JSON payload to any .NET type, while deserializing, the JsonSerializer returns a boxed JsonElement instead that points to the JSON token itself. We don't try to infer or guess the .NET type from the JSON payload during deserialization and hence, the caller (who has enough context) would need to turn the returned JsonElement into the expected .NET type.

This seems to work, but it would be nice if it was built in.

The approach you used makes sense. Alternatively, rather than using a converter up-front while deserializing, you could carry the boxed JsonElement (as object) unprocessed until the point where you need to use the actual object, and turn it into the expected (concrete) type then.

@bartonjs, @steveharter - what are your thoughts on changing the semantics and not box the JSON primitive types as JsonElement (string, number, bool) when deserializing into object (null already has this behavior), and reserving that only for complex types (object and array)?

The approach you used makes sense. Alternatively, rather than using a converter up-front while deserializing, you could carry the boxed JsonElement (as object) unprocessed until the point where you need to use the actual object, and turn it into the expected (concrete) type then.

Thanks for the good feedback 馃槃 , then i am on the right track. In this instance we use the value right away so parsing it late would not be less work.
I see you point, at the same time it is confusing from a user perspective that a value that has a known jsontype get boxed when deserialized.

what are your thoughts on changing the semantics and not box the JSON primitive types as JsonElement (string, number, bool) when deserializing into object

Number is tricky, I wouldn't do it for number. (When coming out of an object context you have to first unbox it, which requires knowing the type it became, then you can convert it to the type you want. Is the progression int/long/ulong, and if there's a '.' use double? It seems rational when describing the functionality, but how does the caller code look?)

If you currently only ever are JsonElement or null, there's probably a customer who has written a hard cast, and you'd now be breaking them. Under in-place upgrade compatibility that'd be forbidden. SxS it might be OK.

Closing - this is a duplicate of https://github.com/dotnet/corefx/issues/38713.

This was a truly shocking breaking change with no documentation :(
This only cost me 4 hours of wasted time 馃憥 馃憥 馃憥 馃憥 馃憥 馃憥

Was this page helpful?
0 / 5 - 0 ratings