Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

JsonObject implementation and tests#40206

Merged
kasiabulat merged 16 commits intodotnet:masterfrom
kasiabulat:kasiabulat/json-object
Aug 14, 2019
Merged

JsonObject implementation and tests#40206
kasiabulat merged 16 commits intodotnet:masterfrom
kasiabulat:kasiabulat/json-object

Conversation

@kasiabulat
Copy link
Copy Markdown
Contributor

I added JsonObject implementation and tests.

addresses: #39922
cc: @joperezr @bartonjs @ericstj @ahsonkhan @terrajobst @JamesNK @stephentoub

JsonObject tests added

JsonObject documentation adde
Copy link
Copy Markdown

@ahsonkhan ahsonkhan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial feedback.

Comment thread src/System.Text.Json/src/Resources/Strings.resx Outdated
Comment thread src/System.Text.Json/src/Resources/Strings.resx Outdated
Comment thread src/System.Text.Json/src/System.Text.Json.csproj Outdated
Comment thread src/System.Text.Json/src/System/Text/Json/Node/DuplicatePropertyNameHandling.cs Outdated
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) { }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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());

Comment thread src/System.Text.Json/tests/JsonObjectTests.cs
Comment thread src/System.Text.Json/tests/JsonObjectTests.cs Outdated
Comment thread src/System.Text.Json/tests/JsonObjectTests.cs Outdated
Comment thread src/System.Text.Json/tests/JsonObjectTests.cs Outdated
Comment thread src/System.Text.Json/tests/NewtonsoftTests/ILLinkTrim.xml Outdated
[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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO JsonObject should implement IDictionary<string, JsonNode> and JsonArray should implement IList<JsonNode>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  • IsReadOnly
  • Keys (but we have PropertyNames),
  • Values (but we have PropertyValues)
  • Clear()
  • CopyTo(T[], Int32)
  • Contains(T)
  • ContainsKey(TKey) (but we have ContainsProperty)
  • TryGetValue(TKey, TValue) (but we have (Try)GetPropertyValue).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amoungst other benefits, IDictionary allows a collection initializer to be used:

var o = new JsonObject
{
    ["Name"] = "James",
    ["Radness"] = 98,
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) { }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+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.

Comment thread src/System.Text.Json/ref/System.Text.Json.cs
Comment thread src/System.Text.Json/ref/System.Text.Json.cs Outdated
Comment thread src/System.Text.Json/ref/System.Text.Json.cs Outdated
Comment thread src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs Outdated
Comment thread src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs Outdated
Comment thread src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs
Comment thread src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs Outdated
Comment thread src/System.Text.Json/src/Resources/Strings.resx Outdated
Comment thread src/System.Text.Json/src/Resources/Strings.resx Outdated
Comment thread src/System.Text.Json/src/Resources/Strings.resx Outdated
Comment thread src/System.Text.Json/src/Resources/Strings.resx Outdated
Comment thread src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs Outdated
Comment thread src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs Outdated
Comment thread src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs Outdated
Comment thread src/System.Text.Json/tests/JsonObjectTests.cs Outdated
Comment thread src/System.Text.Json/tests/JsonObjectTests.cs
Comment thread src/System.Text.Json/src/System/Text/Json/Node/DuplicatePropertyNameHandling.cs Outdated
Comment thread src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs Outdated
Comment thread src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs Outdated
Comment thread src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs Outdated
Comment thread src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs Outdated
Comment thread src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs Outdated
Comment thread src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs Outdated
Comment thread src/System.Text.Json/tests/JsonObjectTests.cs Outdated
Comment thread src/System.Text.Json/tests/JsonObjectTests.cs Outdated
Comment thread src/System.Text.Json/tests/JsonObjectTests.cs Outdated
Comment thread src/System.Text.Json/tests/JsonObjectTests.cs Outdated
Comment thread src/System.Text.Json/src/System.Text.Json.csproj
Comment thread src/System.Text.Json/ref/System.Text.Json.cs Outdated
Comment thread src/System.Text.Json/src/Resources/Strings.resx Outdated
/// <exception cref="InvalidCastException">
/// Property with specified name is not a JSON object.
/// </exception>
public JsonObject GetJsonObjectPropertyValue(string propertyName)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.?

Copy link
Copy Markdown
Contributor Author

@kasiabulat kasiabulat Aug 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/System.Text.Json/tests/JsonObjectTests.cs Outdated
/// 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>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't generic ReadOnlySpan but rather a specific T (in this case char).

Suggested change
/// <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?

Copy link
Copy Markdown
Contributor Author

@kasiabulat kasiabulat Aug 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With "ReadOnlySpan{char}" I got the error "Type parameter declaration must be an identifier not a type."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I knew I had written type-specific generic within xml comments before. It should be uppercase.

Suggested change
/// <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>

/// <summary>
/// Converts a <see cref="ReadOnlySpan{Char}"/> into a StandardFormat
/// </summary>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks, I'll fix it in next PR.

Comment thread src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs Outdated
Comment thread src/System.Text.Json/tests/JsonObjectTests.cs Outdated
@kasiabulat kasiabulat merged commit ceaad92 into dotnet:master Aug 14, 2019
@kasiabulat kasiabulat deleted the kasiabulat/json-object branch August 16, 2019 22:26
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
* JsonObject implementation added
* JsonObject tests added
* JsonObject documentation added
* Review comments included

Commit migrated from dotnet/corefx@ceaad92
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants