Closed justinushermawan closed 3 years ago
No, this feature is not supported at this point but something we should consider for vNext. Do you have an example usage to motivate the feature request?
Marking as future.
System.NotSupportedException : The collection type 'System.Dynamic.ExpandoObject' is not supported.
@ahsonkhan GraphQL is a good example.
The spec recomends JSON but it is not tied to any specific serialization in the response. This implys that the "data" field of the response is of the kind: dynamic. As it can't be infered. Without ExpandoObject the deserialization makes dynamic a type of a JSON making. So accessing to that abstract dynamic "data" must be done knowing that in fact that dynamic is a JToken.
With ExpandoObject I think that we could enforce the access of the dynamic "data" like a common object
@ahsonkhan another example is Configuration Service in our project. It exposes collection like REST endpoints, which create collections in MongoDB (it is not just a dummy REST wrapper, and rest collections and mongo collection do not have exact 1-1 mapping, it also asserts certain rules).
So in our project we need dynamic/ExpandoObject support.
We use it in other microservices as well.
We also ran into this limitation. Our use case is gradually building a dynamic object before json serialization. Went back to the more mature Json.NET serializer.
Hey guys
whats the walk around for now? May i configure which one to use? @SidShetye you said something about went back to more mature one, may you explain please?
@MickeyReznikov : See https://stackoverflow.com/questions/15455304/deserialize-a-property-as-an-expandoobject-using-json-net or google “json.net expandoobject”
@MickeyReznikov, was your question answered? I believe @SidShetye meant going back to using Newtonsoft.Json
for serializing dynamic objects since the in-box library (System.Text.Json) doesn't have support for those yet. In the case of asp.net apps, you can configure it to AddNewtonsoftJson back.
I also have a use-case for this - I just want to call a REST API with HttpClient and retrieve a single property from the response. I do not want to create a dedicated class just to parse the response... With JSON.NET I could just use "dynamic" and access the property of choice.
From https://github.com/dotnet/corefx/issues/41472 by @ghost1372:
hi I hope this is a good place to ask this question It seems we can't deserialize dynamic objects i used this code but not worked is there any way to do this?
var objList = System.Text.Json.JsonSerializer.Deserialize<List<dynamic>>(json);
In our many many applications we are controlling the fields of data from the stored procedures and dynamically render the List and Search List pages with jquery.jtable
Without JsonSerializer built-in support for ExpandoObject we cannot use dotnet core built-in Json Serialization
We do share the same use case as already mentioned by @estiller and @SidShetye We had to switch back to Json.NET in the meantime.
Is it possible to have a milestone closer to now than Future? 🤔
ExpandoObject's been in the BCL for a looooong time
Is there any alternative for ExpandoObject?
@fatihyildizhan No there is no alternate.
but we have written our own ExpandoObject converter, you can take the hint from this article ASP.NET Core 3.0: Custom JsonConverter for the new System.Text.Json
We need only the serialization, so we just create the serialization
I was surprised this isn't supported yet.
Moving this to 5.0.
Moving to 5.0? That means we have to wait for a year at least? Back to JSON.Net it is.
5.0? wow, that definitely sucks.
For temporal ugly workaround I can use JsonDocument with custom converter for it, but it is IDisposable.
public sealed class EventObject
{
[JsonPropertyName("id")]
public long Id
{
get; set;
}
[JsonPropertyName("eventData")]
[JsonConverter(typeof(JsonDocumentConverter))]
public System.Text.Json.JsonDocument EventData
{
get; set;
}
}
internal sealed class JsonDocumentConverter
: JsonConverter<System.Text.Json.JsonDocument>
{
public override JsonDocument Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return JsonDocument.ParseValue(ref reader);
}
public override void Write(Utf8JsonWriter writer, JsonDocument value, JsonSerializerOptions options)
{
value.WriteTo(writer);
}
}
We are no longer able to use the following POST action in the presence of System.Text.Json:
[HttpPost]
public async Task<IActionResult> SubmitAsync(dynamic model)
Instead, we had to use the following method but there was no straight forward to use 'model' in the downstream backend code:
[HttpPost]
public async Task<IActionResult> SubmitAsync(JsonElement model)
'model' is a complex object and it may contain a collection of objects and/or other nested complex objects. To be able to conclude a dynamic
object out of JsonElement
, we had to call model.GetRawText() and have Newtonsoft decode it into a dynamic object. This way is not the desired way because the main purpose of this exercise was to decommission Newtonsoft.json from the project.
Can I assume that addressing this ticket/issue implies a fix for our issue that we've been experiencing? It seems to be a bit urgent issue to address, so can it be addressed sooner than later?
/cc @ahsonkhan @terrajobst
JsonSerializer support for ExpandoObject(Interim measures) I am newbie, many places are not perfect, welcome everyone to modify .net Core3 no support
add using:
/// <summary>
/// Temp Dynamic Converter
/// by:tchivs@live.cn
/// </summary>
public class DynamicJsonConverter : JsonConverter<dynamic>
{
public override dynamic Read(ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.True)
{
return true;
}
if (reader.TokenType == JsonTokenType.False)
{
return false;
}
if (reader.TokenType == JsonTokenType.Number)
{
if (reader.TryGetInt64(out long l))
{
return l;
}
return reader.GetDouble();
}
if (reader.TokenType == JsonTokenType.String)
{
if (reader.TryGetDateTime(out DateTime datetime))
{
return datetime;
}
return reader.GetString();
}
if (reader.TokenType == JsonTokenType.StartObject)
{
using JsonDocument documentV = JsonDocument.ParseValue(ref reader);
return ReadObject(documentV.RootElement);
}
// Use JsonElement as fallback.
// Newtonsoft uses JArray or JObject.
JsonDocument document = JsonDocument.ParseValue(ref reader);
return document.RootElement.Clone();
}
private object ReadObject(JsonElement jsonElement)
{
IDictionary<string, object> expandoObject = new ExpandoObject();
foreach (var obj in jsonElement.EnumerateObject())
{
var k = obj.Name;
var value = ReadValue(obj.Value);
expandoObject[k] = value;
}
return expandoObject;
}
private object? ReadValue(JsonElement jsonElement)
{
object? result = null;
switch (jsonElement.ValueKind)
{
case JsonValueKind.Object:
result = ReadObject(jsonElement);
break;
case JsonValueKind.Array:
result = ReadList(jsonElement);
break;
case JsonValueKind.String:
//TODO: Missing Datetime&Bytes Convert
result = jsonElement.GetString();
break;
case JsonValueKind.Number:
//TODO: more num type
result = 0;
if (jsonElement.TryGetInt64(out long l))
{
result = l;
}
break;
case JsonValueKind.True:
result = true;
break;
case JsonValueKind.False:
result = false;
break;
case JsonValueKind.Undefined:
case JsonValueKind.Null:
result = null;
break;
default:
throw new ArgumentOutOfRangeException();
}
return result;
}
private object? ReadList(JsonElement jsonElement)
{
IList<object?> list = new List<object?>();
foreach (var item in jsonElement.EnumerateArray())
{
list.Add(ReadValue(item));
}
return list.Count == 0 ? null : list;
}
public override void Write(Utf8JsonWriter writer,
object value,
JsonSerializerOptions options)
{
// writer.WriteStringValue(value.ToString());
}
}
var serializerOptions = new JsonSerializerOptions
{
Converters = { new DynamicJsonConverter() }
};
return JsonSerializer.Deserialize<dynamic>("{OK:"200"}", serializerOptions);
@tchivs, your solution worked for me; but since the Converters property is read-only, I had to do something like this:
var serializerOptions = new JsonSerializerOptions();
serializerOptions.Converters.Add(new DynamicJsonConverter());
return JsonSerializer.Deserialize<dynamic>("{OK:"200"}", serializerOptions);
Try using the JsonElement type:
public JsonElement MyProperty {get; set;}
@tchivs, I made some modifications to your code -- reworking it so that it uses dynamically generated "projection" types (having a subset of properties from a base type), rather than ExpandoObject. I posted the code (in a sample console project) here: EDennis.DynamicDeserialization.
I will be testing this approach with more complicated objects and under various scenarios (e.g., deserialization of dynamic objects used to patch existing, fully typed EF Core entities; deserialization of json objects and arrays for test cases). Let me know if you find this useful or if you see anything problematic.
Thanks for the community workaround. It's surprising Microsoft are unable to put out this feature in a sensible timeframe. Working with GraphQL responses without dynamic object of some sort leads to lots of verbose and ugly code. Or even just checking for existence of deeply nested properties.
I read through the thread of comments and most are focused on deserialization, I'm facing an issue where also serialization of dynamic objects apparently silently "fails". In an attempt to reproduce my scenario I came up with the following minimal repro:
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Text.Json;
namespace SampleConsoleApp
{
class Program
{
static void Main(string[] args)
{
dynamic d = new CustomDynamicObject();
d.AProperty = 10;
var s = JsonSerializer.Serialize(d);
Console.WriteLine(s);
Console.Read();
}
}
class CustomDynamicObject : DynamicObject
{
private readonly IDictionary<string, object> properties = new Dictionary<string, object>();
public override bool TryGetMember(GetMemberBinder binder, out object result) => properties.TryGetValue(binder.Name, out result);
public override bool TrySetMember(SetMemberBinder binder, object value)
{
properties[binder.Name] = value;
return true;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
foreach (var item in properties.Keys)
{
yield return item;
}
}
}
}
When run s
is {}
, so serialization doesn't fail but yields an empty json object.
Is this the right issue? or should I raise/follow a different one?
Is this the right issue? or should I raise/follow a different one?
This is the right issue. No need to open one for the two halves of the same feature. This issue is about adding support for expando object (which means for both sides, serialize and deserialize).
I've met this issue today - wanted to say that JsonElement
would work fine if it didn't depend on disposed JsonDocument
. One way I can think of going around this issue is to implement a destructor for JsonDocument
so it's disposal can be postponed to later time - once all JsonElement
objects are collected.
I also needed a dynamic object. However, it's not implemented yet. My solution was to use a dictionary.
JsonSerializer.Deserialize<Dictionary<string, string>>(response)
And then lookup for the key I need :)
Just for my own sanity - is the issue scoped for 5.0 specifically ExpandoObject support or the ability to deserialize into a dynamic object? The conversation here doesn't all seem to match the issue title, and I'm definitely needing the latter. In my case I'm deserializing a nested dynamic, specifically List<Dictionary<string, dynamic>>
. Switched back to Newtonsoft for now :(
just wanted to share some c# 8 syntax sugar mods of @tchivs helpful code :)
/// <summary>
/// Temp Dynamic Converter with c# 8
/// by:tchivs@live.cn
/// </summary>
public class DynamicJsonConverter : JsonConverter<dynamic>
{
public override dynamic Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> reader.TokenType switch
{
JsonTokenType.True => true,
JsonTokenType.False => false,
JsonTokenType.Number => reader.TryGetInt64(out long l) ? 1 : reader.GetDouble(),
JsonTokenType.String => reader.TryGetDateTime(out DateTime datetime) ? datetime.ToString() : reader.GetString(),
JsonTokenType.StartObject => ReadObject(JsonDocument.ParseValue(ref reader).RootElement),
// Use JsonElement as fallback.
_ =>JsonDocument.ParseValue(ref reader).RootElement.Clone()
};
private object ReadObject(JsonElement jsonElement)
{
IDictionary<string, object> expandoObject = new ExpandoObject();
foreach (var obj in jsonElement.EnumerateObject())
{
var k = obj.Name;
var value = ReadValue(obj.Value);
expandoObject[k] = value;
}
return expandoObject;
}
private object? ReadValue(JsonElement jsonElement)
=>
jsonElement.ValueKind switch
{
JsonValueKind.Object => ReadObject(jsonElement),
JsonValueKind.Array => ReadList(jsonElement),
JsonValueKind.String => jsonElement.GetString(),
JsonValueKind.Number => jsonElement.TryGetInt64(out long l) ? 1 :0,
JsonValueKind.True => true,
JsonValueKind.False =>false,
JsonValueKind.Undefined => null,
JsonValueKind.Null => null,
_ => throw new ArgumentOutOfRangeException()
};
private object? ReadList(JsonElement jsonElement)
{
var list = new List<object?>();
jsonElement.EnumerateArray().ToList().ForEach(j => list.Add(ReadValue(j)));
return list.Count == 0 ? null : list;
}
public override void Write(Utf8JsonWriter writer,
object value,
JsonSerializerOptions options)
{
// writer.WriteStringValue(value.ToString());
}
}
@rs38 thanks for the code here, was exactly what I needed. Wanted to point on a very subtle but important change needed. The two lines that parse the "Number" type are incorrect in your compressed version:
JsonTokenType.Number => reader.TryGetInt64(out long l) ? 1 : reader.GetDouble(),
should be
JsonTokenType.Number => reader.TryGetInt64(out long l) ? l : reader.GetDouble(),
JsonValueKind.Number => jsonElement.TryGetInt64(out long l) ? 1 :0,
should be
JsonValueKind.Number => jsonElement.TryGetInt64(out long l) ? l :0,
@layomia This is not serious. Support should have been done a long time ago (in fact System.Text.Json shouldn't have been launched without it in my opinion)! And we don't even have a deadline!
My scenario is about CosmosDB. I'm querying using the new .NET SDK wich uses this JsonSerializer and since it is a schemaless database I don't want to create a class for each projection of the database data I need to do (there are a bunch of different queries). I need the results of the queries as lists of dynamic objects.
@SocVi100 Do not put your hopes on Microsoft for this one. Better stick with Newtonsoft's Json.net. @layomia has flushed all hopes for an near future integration. It seems they don't really care about the developer's needs. First they shouted "forget about json.net, we got you covered!" Luckily, developers didn't forget about json.net, or the alikes.
why don't you contribute? implement it and put in a PR!
https://github.com/dotnet/runtime/blob/master/docs/coding-guidelines/adding-api-guidelines.md https://github.com/dotnet/runtime/blob/master/docs/area-owners.md (Lead: @ericstj |, owners: @layomia @steveharter @jozkee)
why don't you contribute? implement it and put in a PR!
I wish I could.
My scenario is about CosmosDB. I'm querying using the new .NET SDK wich uses this JsonSerializer and since it is a schemaless database I don't want to create a class for each projection of the database data I need to do (there are a bunch of different queries). I need the results of the queries as lists of dynamic objects.
It's possible to configure a different serializer, using CosmosClientBuilder.WithCustomSerializer.
Here is an example: CosmosJsonNetSerializer
We couldn't fit this feature into 5.0. We had to prioritize along with the rest of the 5.0 feature work, and this one didn't fit. FWIW this was very close to the cut line, thus the late move.
@layomia has flushed all hopes for an near future integration. It seems they don't really care about the developer's needs. First they shouted "forget about json.net, we got you covered!" Luckily, developers didn't forget about json.net, or the alikes.
@RobbyDeLaet can we try to keep this discussion constructive? We are trying to do our best to respond to top requested customer features while maintaining design principles. With respect to feature parity with Newtonsoft.Json, here's what we have to say.
System.Text.Json focuses primarily on performance, security, and standards compliance. It has some key differences in default behavior and doesn't aim to have feature parity with Newtonsoft.Json. For some scenarios, System.Text.Json has no built-in functionality, but there are recommended workarounds. For other scenarios, workarounds are impractical. If your application depends on a missing feature, consider filing an issue to find out if support for your scenario can be added.
We don't aim to replace Newtonsoft.JSON. If it works for you, continue to use it. We'll do our best to make System.Text.Json as useful as possible while maintaining the gains we've achieved in performance, security, standards compliance, and layering. Over time we hope to make it work for as many folks as possible. I am hearing the interest here and will make sure we focus on this moving forward.
Thanks for your suggestions. I've finally gone by the middle, using Newtonsoft serializer only for deserialization, but I still have to test. I hope it doesn't last too much to get ExpandoObject support on the default serializer of CosmosDB so I could get rid of the old one. @RobbyDeLaet, thanks for your comment! I don't have much expectatives about Microsoft implementing anyting, I've felt fustrated too much times about them. As a freelance developer I've waited for years, literally, for essential features to be implemented on CosmosDB, EntityFramework and Identity infrastructure, and they make their own way ignoring us. It doesn't matter how many votes they have on the UserVoice or whatever. I supose that such features aren't needed for it's own developments so are out of scope... The problem is always the same: Too much marketing = too much expectatives
To help those that need this functionality, see the code samples above to help unblock:
ExpandoObject
for objects and List<object>
for collections)ExpandoObject
)To help with requirements for this feature I will provide a new custom converter sample and will link that here when it's ready. After that I'll get that sample added to the Newtonsoft work-around section. cc @tdykstra
One detail not yet discussed is that supporting dynamic
requires a reference to the very large System.Linq.Expressions.dll
assembly; this complication may mean in the future we add the dynamic converter in a new assembly (e.g. System.Text.Json.Converters.dll
) to get pay-to-play for those that care about deployment size (such as a self-contained app or a Blazor client app).
Just for my own sanity - is the issue scoped for 5.0 specifically ExpandoObject support or the ability to deserialize into a dynamic object? The conversation here doesn't all seem to match the issue title, and I'm definitely needing the latter. In my case I'm deserializing a nested dynamic, specifically
List<Dictionary<string, dynamic>>
. Switched back to Newtonsoft for now :(
I am now testing an old function I implemented to deserialize nested objects and seeing the same issue noted in this topic with "valueKind".
Was there a way to fix this using the new neetonsoft.json packages? Because the above code worked for the root level object but not for the nested objects.
@ericstj Apologies, my reaction was perhaps a bit too negative and harsh, but like @SocVi100 I'm a freelance developer and sometimes frustrations get the upper hand. But at least we got a discussion started here. Thank you for your response, now we got at least a clear message about the status of this request.
Just for my own sanity - is the issue scoped for 5.0 specifically ExpandoObject support or the ability to deserialize into a dynamic object
I assume the desired semantics are to have a dynamic type so after deserializing you can access all properties (including nested) in a late-bound manner:
dynamic obj = JsonSerializer.Deserialize<dynamic>(json);
string s = obj.MyChildProperty.MyStringList[2];
and I assume the desired semantics also support ExpandoObject
explicitly:
ExpandoObject expando = JsonSerializer.Deserialize<ExpandoObject>(json);
dynamic obj = expando;
string s = obj.MyChildProperty.MyStringList[2];
So my understanding of the scope:
Deserialize<dynamic>(json)
returns either a primitive, collection or object. An object here will likely be ExpandoObject
but could be IDynamicMetaObjectProvider
or a JITed type depending on implementation. This is different than today which always returns JsonElement
.Deserialize<ExpandoObject>(json)
(which implements IDynamicMetaObjectProvider
) should create a proper ExpandoObject
which in turn can be used with dynamic
. This is different than today which only creates expando-properties for the root properties and JsonElement
instances for all nested properties.Serialize<ExpandoObect>()
works as expectedSerialize<IDynamicMetaObjectProvider
()> may or may not be implemented depending on whether there are scenarios where ExpandoObject
is not used.Semantics in 3.0 - 5.0:
ExpandoObject
works since ExpandoObject
implements IDictionary<string, object>
and STJ will correctly serialize each object
value whether it is a primitive, collection or object.ExpandoObject
sort of works but only the root properties are "proper" expando-properties; any nested properties will be JsonElement
so there is inconsistency in the programming model thus deserializing ExpandoObject
should be avoided unless a custom converter is used for that.Options for 3.0-5.0:
ExpandoObject
and\or object
as mentioned in the posts above; I'll provide a link soon to a new sample that ideally maps to a 6.0 feature.ExpandoObject
(or dynamic
if the type is based on ExpandoObject
). Since deserializing ExpandoObject
has an inconsistency issue today in STJ, ExpandoObject
is not recommended if deserializing.JsonElement
instead of dynamic objects. JsonElement
doesn't attempt to "guess" what type the JSON maps to, so you have to be explicit by calling GetString(), GetInt32(), etc.Also, depending on implementation, the implementation of a custom converter likely means there is some "guessing" about what CLR types the JSON maps to during deserialization. For example, a JSON string may map to either a DateTime
or a string
, a JSON number may map to a double
or a long
, and the JSON array type needs to be determined. This needs to be vetted and compared against Newtonsoft semantics. This should also align to what type is returned when a property is of Type object
(today it is JsonElement
).
Here's some examples of 3.0 - 5.0 STJ semantics:
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Diagnostics;
using System.Linq;
using System.Text.Json;
namespace ConsoleApp
{
class Program
{
const string ExpectedJson = "{\"A\":\"A\",\"B\":[1,2],\"C\":42,\"D\":\"2020-01-01T00:00:00\",\"E\":{\"A_Child\":\"A_Child\"}}";
static void Main(string[] args)
{
DateTime dateTime = new DateTime(2020, 1, 1);
dynamic myDynamicChild = new ExpandoObject();
myDynamicChild.A_Child = "A_Child";
dynamic myDynamic = new ExpandoObject();
myDynamic.A = "A";
myDynamic.B = new List<int>() { 1, 2 };
myDynamic.C = 42;
myDynamic.D = dateTime;
myDynamic.E = myDynamicChild;
// Verify we can call late-bound property.
int c = myDynamic.C;
Debug.Assert(c == 42);
// STJ can serialize with ExpandoObject since it implements IDictionary<string, object>.
string json = JsonSerializer.Serialize<ExpandoObject>(myDynamic);
Debug.Assert(json == ExpectedJson);
// Using 'dynamic' against backing ExpandoObject works.
json = JsonSerializer.Serialize<dynamic>(myDynamic);
Debug.Assert(json == ExpectedJson);
// Deserialize with <dynamic>, <object> and <JsonElement>.
// For 5.0, using one of these is recommended over ExpandoObject because the programming model will be
// consistent for the root type and all nested types.
// Using <JsonElement> makes it clear and non-abiguous.
// Using <object> by default uses 'JsonElement', but can be overridden by a custom converter.
// Using <dynamic> uses 'object' which uses 'JsonElement'.
{
dynamic d = JsonSerializer.Deserialize<dynamic>(json);
VerifyJsonElement(d);
try
{
// We will get an exception here if we try to access a dynamic property since 'object' is deserialized
// as a JsonElement and not an ExpandoObject.
c = d.C;
Debug.Fail("Should have thrown Exception!");
}
catch (Exception ex)
{
Debug.Assert(ex.Message == "'System.Text.Json.JsonElement' does not contain a definition for 'C'");
}
// Serializing with <object> creates a JsonElement by default (can be changed by a custom converter).
object o = JsonSerializer.Deserialize<object>(json);
VerifyJsonElement((JsonElement)o);
// Serialize with explicit <JsonElement>.
JsonElement e = JsonSerializer.Deserialize<JsonElement>(json);
VerifyJsonElement(e);
}
// Deserialize with ExpandoObject. This creates an ExpandoObject with the root Type having Expando-properties
// but the value of those properties will be JsonElement. All other nested properties\objects\collections will
// also be JsonElement. Due to the inconsistency of having only root-level Expando-properties (such as A_Child),
// deserializing as ExpandoObject is not recommended (unless a ExpandoObject custom converter is used).
{
// When STJ deserializes, it creates ExpandoObjects via IDictionary<string, object> where 'object' is JsonElement.
ExpandoObject expando = JsonSerializer.Deserialize<ExpandoObject>(json);
Debug.Assert(((IDictionary<string, object>)expando).Keys.Count == 5);
myDynamic = expando;
JsonElement jsonElement = myDynamic.A;
Debug.Assert(jsonElement.GetString() == "A");
jsonElement = myDynamic.B;
Debug.Assert(jsonElement.EnumerateArray().Count() == 2);
jsonElement = myDynamic.C;
Debug.Assert(jsonElement.GetInt32() == 42);
jsonElement = myDynamic.D;
Debug.Assert(jsonElement.GetDateTime() == dateTime);
jsonElement = myDynamic.E;
// Here we have an inconsistency. Nested object property must use JsonElement (not a dynamic property).
Debug.Assert(jsonElement.GetProperty("A_Child").GetString() == "A_Child");
// Re-serialize works as expected.
json = JsonSerializer.Serialize<ExpandoObject>(myDynamic);
Debug.Assert(json == ExpectedJson);
// Re-serialize works as expected; dynamic works here since backed by ExpandoObject in this example.
json = JsonSerializer.Serialize<dynamic>(myDynamic);
Debug.Assert(json == ExpectedJson);
}
void VerifyJsonElement(JsonElement elem)
{
// Verify JsonElement
Debug.Assert(elem.GetProperty("A").GetString() == "A");
Debug.Assert(elem.GetProperty("B").EnumerateArray().Count() == 2);
Debug.Assert(elem.GetProperty("C").GetInt32() == 42);
Debug.Assert(elem.GetProperty("D").GetDateTime() == dateTime);
Debug.Assert(elem.GetProperty("E").GetProperty("A_Child").GetString() == "A_Child");
// Re-serialize
json = JsonSerializer.Serialize<dynamic>(elem);
Debug.Assert(json == ExpectedJson);
json = JsonSerializer.Serialize<JsonElement>(elem);
Debug.Assert(json == ExpectedJson);
}
}
}
}
@rs38 Can you fix your code snippet based on what @ryan-hollister-q2 pointed out?
As promised, the PR providing a dynamic implementation sample is at https://github.com/dotnet/runtime/pull/42097.
@rs38 Can you fix your code snippet based on what @ryan-hollister-q2 pointed out?
that wasn't my code, just shared some snippets from @tchivs earlier in this thread.
I saw many people in this issue talk about the dynamic
but what about the Writable JSON DOM ?
As a Newtonsoft.Json user, I won't use dynamic
, instead I use JToken
, because in many case the json is data: T | T[]
,
and to handle those data, you can't use just dynamic
because it hard to know what is the type of the object.
the JToken
solve 99% of the serialize/deserialize problem , and we can "corrent" the JToken
to right form (or add necessary other properties ), and then send to other place (eg. return to client or write to db or some message handler)
I saw many people in this issue talk about the dynamic but what about the Writable JSON DOM ?
The proposal for 6.0 is to support for dynamic
and non-dynamic
cases with an overlapping but consistent programming model. Newtsoft does this as well since JToken
implements IDynamicMetaObjectProvider
which supports dynamic
. This means if you don't want to use dynamic
, you don't have to. In general, I would expect most consumers to not use dynamic
since it is slower.
and we can "corrent" the JToken to right form
Can you elaborate on "correcting" to make sure we have the scenario. I assume you mean changing the DOM to modify a value of a property\element or by adding\removing properties\elements.
@steveharter
Can you elaborate on "correcting" to make sure we have the scenario. I assume you mean changing the DOM to modify a value of a property\element or by adding\removing properties\elements.
yes, for example change the data:T|T[]
to data:T[]
. I can't do it (or hard) with dynamic
because I need to know the type of the property/object , and JToken
can do this perfectly because every property/object have a TokenType
property.
but today the JsonDocument
JsonElement
is just a StringReader
or SpanReader
(compare to newtonsoft.Json is new JsonTextReader(new StringReader(ref jsonString))
, just that today we can't pass by ref
) ,
and it seems that this issue became to use dynamic
to serialize and deserialize the object again and again, it will be way slower than JToken
like "C# object" , and hard to detect it's type .
we don't need a string reader
we need the "C# object" !
Does
JsonSerializer.Parse(String, Type, JsonSerializerOptions)
support for dynamicExpandoObject
return type?Something like this:
dynamic p = JsonSerializer.Parse(json, typeof(ExpandoObject));