JsonObject implementation and tests#40206
Conversation
JsonObject tests added JsonObject documentation adde
| public sealed partial class JsonObject : System.Text.Json.JsonNode, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, System.Text.Json.JsonNode>>, System.Collections.IEnumerable | ||
| { | ||
| public JsonObject(System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, System.Text.Json.JsonNode>> jsonProperties, System.Text.Json.DuplicatePropertyNameHandling duplicatePropertyNameHandling = System.Text.Json.DuplicatePropertyNameHandling.Replace) { } | ||
| public JsonObject(System.Text.Json.DuplicatePropertyNameHandling duplicatePropertyNameHandling = System.Text.Json.DuplicatePropertyNameHandling.Replace) { } |
There was a problem hiding this comment.
I wouldn't have expected DuplicatePropertyNameHandling to show up within JsonObject's API surface, but rather within the options passed in to the type that creates JsonObjects from some json payload (say JsonDocumentOptions).
Do we think there will be a need for this setting when creating a JsonObject itself via the ctor? What scenario would it be used for?
There was a problem hiding this comment.
+1
People who are manually creating a JsonObject in code can choose whether to use o.Add("name", "value") or o["name"] = "value". That choice is basically the equivalent of duplicating property handling.
There was a problem hiding this comment.
So actually, my Add behaves as specified in DuplicatePropertyNameHandling, to be consistent. So while indexer will always replace values, Add will either throw / replace or ignore (defaultly would behave just as indexer, but I can change the default behavior to exceptions).
Maybe we could have those options both in JsonObject and passed to parsing method? I think people might want to specify the behavior they want also when creating objects from C# - what this API is designed for?
There was a problem hiding this comment.
I think people might want to specify the behavior they want also when creating objects from C# - what this API is designed for?
Can you give an example/sample where you think it would be useful to have DuplicatePropertyNameHandling when creating JsonObject?
There was a problem hiding this comment.
For example something containing interactions with user?
var properties = new JsonObject(DuplicatePropertyNameHandling.Error);
while(PropertiesReady())
{
string userProvidedPropertyName = Console.ReadLine();
string userProvidedPropertyValue = Console.ReadLine();
try
{
properties.Add(userProvidedPropertyName, userProvidedPropertyValue);
}
catch (ArgumentException)
{
Console.WriteLine(string.Format("Property name `{0}` already exists. Please provide properties with unique names.", userProvidedPropertyName));
}
}
Mailbox.SendData(properties.AsJsonElement());| [System.CLSCompliantAttribute(false)] | ||
| public bool TryGetUInt64(out ulong value) { throw null; } | ||
| } | ||
| public sealed partial class JsonObject : System.Text.Json.JsonNode, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, System.Text.Json.JsonNode>>, System.Collections.IEnumerable |
There was a problem hiding this comment.
IMO JsonObject should implement IDictionary<string, JsonNode> and JsonArray should implement IList<JsonNode>
There was a problem hiding this comment.
JsonArray is planned to implement IList, but we decided not to make JsonObject implement IDictionary, because it would be then possible to pass it for methods that accepts a dictionary, which we didn't find useful and desired. @bartonjs ?
There was a problem hiding this comment.
@kasiabulat are there any methods from IDictionary that we would be missing if we wanted to implement that? Are there any Sort methods in IDictionary?
There was a problem hiding this comment.
There are no extension methods accepting IDictionary / IList instead of IEnumerable. LINQ queries work faster with IList and that's why making JsonArray implement it seemed valuable. No queries work faster with IDictionary - that's also why we thought implementing it by JsonObject would be unnecessary.
To implement IDictionary we are lacking:
IsReadOnlyKeys(but we havePropertyNames),Values(but we havePropertyValues)Clear()CopyTo(T[], Int32)Contains(T)ContainsKey(TKey)(but we haveContainsProperty)TryGetValue(TKey, TValue)(but we have(Try)GetPropertyValue).
There was a problem hiding this comment.
FYI use explicit implementation for the dictionary APIs people will likely never use - https://softwareengineering.stackexchange.com/questions/136319/whats-the-difference-between-implementing-an-interface-explicitly-or-implicitly
There was a problem hiding this comment.
I think my point was that unless there were compelling extension methods or other specific use cases that it probably didn't need to implement the IDictionary interface. e.g. that one should be able to say "It implements IDictionary and IReadOnlyDictionary because [reason]" where "[reason]" is something more concrete than "because it's logically a dictionary" or "because it already had all the right methods".
Same result, different presentation: If no one will ever use the fact that it's an I(ReadOnly)Dictionary then it didn't need to assert the interface.
There was a problem hiding this comment.
Amoungst other benefits, IDictionary allows a collection initializer to be used:
var o = new JsonObject
{
["Name"] = "James",
["Radness"] = 98,
}There was a problem hiding this comment.
Amoungst other benefits, IDictionary allows a collection initializer to be used:
That doesn't require IDictionary. Just IEnumerable + suitable Add methods, which it already has 😄
| public sealed partial class JsonObject : System.Text.Json.JsonNode, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, System.Text.Json.JsonNode>>, System.Collections.IEnumerable | ||
| { | ||
| public JsonObject(System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, System.Text.Json.JsonNode>> jsonProperties, System.Text.Json.DuplicatePropertyNameHandling duplicatePropertyNameHandling = System.Text.Json.DuplicatePropertyNameHandling.Replace) { } | ||
| public JsonObject(System.Text.Json.DuplicatePropertyNameHandling duplicatePropertyNameHandling = System.Text.Json.DuplicatePropertyNameHandling.Replace) { } |
There was a problem hiding this comment.
+1
People who are manually creating a JsonObject in code can choose whether to use o.Add("name", "value") or o["name"] = "value". That choice is basically the equivalent of duplicating property handling.
| /// <exception cref="InvalidCastException"> | ||
| /// Property with specified name is not a JSON object. | ||
| /// </exception> | ||
| public JsonObject GetJsonObjectPropertyValue(string propertyName) |
There was a problem hiding this comment.
Should this be returning a JsonNode instead, rather than throwing? What can I do if I want to get the value of a property that happens to be a number, string, array, etc.?
There was a problem hiding this comment.
There's a method GetPropertyValue as well, working for all types of nodes, GetJsonObjectPropertyValue is just accelerating method for accessing nested objects. We were thinking of adding GetJsonArrayPropertyValue as well.
| /// Adds the specified property as a <see cref="JsonString"/> to the JSON object. | ||
| /// </summary> | ||
| /// <param name="propertyName">Name of the property to add.</param> | ||
| /// <param name="propertyValue"><see cref="ReadOnlySpan{T}"/> value of the property to add.</param> |
There was a problem hiding this comment.
This isn't generic ReadOnlySpan but rather a specific T (in this case char).
| /// <param name="propertyValue"><see cref="ReadOnlySpan{T}"/> value of the property to add.</param> | |
| /// <param name="propertyValue"><see cref="ReadOnlySpan{char}"/> value of the property to add.</param> |
@mairaw, @rpetrusha - this is how we format the xml comments for a specific T on a generic type, correct?
There was a problem hiding this comment.
With "ReadOnlySpan{char}" I got the error "Type parameter declaration must be an identifier not a type."
There was a problem hiding this comment.
I knew I had written type-specific generic within xml comments before. It should be uppercase.
| /// <param name="propertyValue"><see cref="ReadOnlySpan{T}"/> value of the property to add.</param> | |
| /// <param name="propertyValue"><see cref="ReadOnlySpan{Char}"/> value of the property to add.</param> |
corefx/src/Common/src/CoreLib/System/Buffers/StandardFormat.cs
Lines 69 to 71 in 36f1a74
There was a problem hiding this comment.
Ok, thanks, I'll fix it in next PR.
* JsonObject implementation added * JsonObject tests added * JsonObject documentation added * Review comments included Commit migrated from dotnet/corefx@ceaad92
I added
JsonObjectimplementation and tests.addresses: #39922
cc: @joperezr @bartonjs @ericstj @ahsonkhan @terrajobst @JamesNK @stephentoub