Support resolve reference for typeof(object) on deserialize#38979
Support resolve reference for typeof(object) on deserialize#38979jozkee merged 1 commit intodotnet:masterfrom
Conversation
| // There are more properties in an object with $ref. | ||
| ThrowHelper.ThrowJsonException_MetadataReferenceObjectCannotContainOtherProperties(); | ||
| } | ||
| else if (property.EscapedNameEquals(s_refPropertyName)) |
There was a problem hiding this comment.
Looks like we lose the validation that the metadata in the payload was the unescaped literal "$ref". Do we care?
There was a problem hiding this comment.
Hmmm not really, unlike NameEquals; EscapedNameEquals will compare against the raw bytes, i.e. it won't unescape the name.
| ref reader); | ||
| } | ||
|
|
||
| if (CanBePolymorphic && options.ReferenceHandler != null) |
There was a problem hiding this comment.
Could another approach be to intercept the call to the Object/JsonElementConverter (when the ref handling feature is on) and just read the $ref metadata directly?
There was a problem hiding this comment.
Yes, but we would have to create a copy of the reader that we would need to throw away if the payload doesn't met the required conditions (being an object, contain only the $ref property). Since this is an edge case, I think this approach is cleaner.
| JsonTestHelper.AssertJsonEqual(expectedjson, serialized); | ||
|
|
||
| memoryStream.Position = 0; | ||
| await TestDeserialization<TElement>(memoryStream, expectedjson, type, options); |
There was a problem hiding this comment.
Why was this line deleted?
There was a problem hiding this comment.
It is still there on line 72, I added the condition if (options.ReferenceHandler == null || !GetTypesNonRoundtrippableWithReferenceHandler().Contains(type)) to skip it given that it won't work if the root type uses object type elements and has a $ref object.
| typeof(GenericIReadOnlyDictionaryWrapper<string, TElement>) | ||
| }; | ||
|
|
||
| // Non-generic types cannot roundtrip when they contain a $ref written on serialization and they are the root type. |
There was a problem hiding this comment.
What's a minimal code sample to highlight this behavior?
There was a problem hiding this comment.
key1 will be parsed as a JsonElement and when key2 tries to look for the reference, it will not find it.
using System;
using System.Collections;
using System.Text.Json;
using System.Text.Json.Serialization;
public class Program
{
public static void Main()
{
var foo = new Foo();
Hashtable h = new Hashtable();
h.Add("key1", foo);
h.Add("key2", foo);
var opts = new JsonSerializerOptions();
opts.ReferenceHandler = ReferenceHandler.Preserve;
string json = JsonSerializer.Serialize(h, opts);
Console.WriteLine(json);
// {"$id":"1","key1":{"$id":"2"},"key2":{"$ref":"2"}}
Hashtable copyOfh = JsonSerializer.Deserialize<Hashtable>(json, opts);
}
}
Fixes #1776
Analyze the
JsonElementto be returned, determines if it can be interpreted as a reference ({"$ref":"1"}), lookup for the reference and returns it instead.This is performed to enable round-tripping on scenarios where a property that is
typeof(object)is a reference to an instance of another type e.g:This change also grants parity with Json.NET in said scenario.