Skip to content
Merged
3 changes: 3 additions & 0 deletions src/libraries/System.Text.Json/ref/System.Text.Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,8 @@ public sealed partial class JsonObject : System.Text.Json.Nodes.JsonNode, System
System.Collections.Generic.KeyValuePair<string, System.Text.Json.Nodes.JsonNode?> System.Collections.Generic.IList<System.Collections.Generic.KeyValuePair<string, System.Text.Json.Nodes.JsonNode?>>.this[int index] { get { throw null; } set { } }
public void Add(System.Collections.Generic.KeyValuePair<string, System.Text.Json.Nodes.JsonNode?> property) { }
public void Add(string propertyName, System.Text.Json.Nodes.JsonNode? value) { }
public bool TryAdd(string propertyName, JsonNode? value) { throw null; }
public bool TryAdd(string propertyName, JsonNode? value, out int index) { throw null; }
public void Clear() { }
public bool ContainsKey(string propertyName) { throw null; }
public static System.Text.Json.Nodes.JsonObject? Create(System.Text.Json.JsonElement element, System.Text.Json.Nodes.JsonNodeOptions? options = default(System.Text.Json.Nodes.JsonNodeOptions?)) { throw null; }
Expand All @@ -870,6 +872,7 @@ public void SetAt(int index, System.Text.Json.Nodes.JsonNode? value) { }
void System.Collections.Generic.IList<System.Collections.Generic.KeyValuePair<string, System.Text.Json.Nodes.JsonNode?>>.RemoveAt(int index) { }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
public bool TryGetPropertyValue(string propertyName, out System.Text.Json.Nodes.JsonNode? jsonNode) { throw null; }
public bool TryGetPropertyValue(string propertyName, out System.Text.Json.Nodes.JsonNode? jsonNode, out int index) { throw null; }
public override void WriteTo(System.Text.Json.Utf8JsonWriter writer, System.Text.Json.JsonSerializerOptions? options = null) { }
}
public abstract partial class JsonValue : System.Text.Json.Nodes.JsonNode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,46 @@ public void Add(string propertyName, JsonNode? value)
value?.AssignParent(this);
}

/// <summary>
/// Adds an element with the provided name and value to the <see cref="JsonObject"/>, if a property named <paramref name="propertyName"/> doesn't already exist.
/// </summary>
/// <param name="propertyName">The property name of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
/// <exception cref="ArgumentNullException"><paramref name="propertyName"/> is null.</exception>
/// <returns>
/// <see langword="true"/> if the property didn't exist and the element was added; otherwise, <see langword="false"/>.
/// </returns>
public bool TryAdd(string propertyName, JsonNode? value) => TryAdd(propertyName, value, out _);

/// <summary>
/// Adds an element with the provided name and value to the <see cref="JsonObject"/>, if a property named <paramref name="propertyName"/> doesn't already exist.
/// </summary>
/// <param name="propertyName">The property name of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
/// <param name="index">The index of the added or existing <paramref name="propertyName"/>. This is always a valid index into the <see cref="JsonObject"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="propertyName"/> is null.</exception>
/// <returns>
/// <see langword="true"/> if the property didn't exist and the element was added; otherwise, <see langword="false"/>.
/// </returns>
public bool TryAdd(string propertyName, JsonNode? value, out int index)
{
if (propertyName is null)
{
ThrowHelper.ThrowArgumentNullException(nameof(propertyName));
}
#if NET9_0
bool success = Dictionary.TryAdd(propertyName, value);
index = success ? Dictionary.Count - 1 : Dictionary.IndexOf(propertyName);
#else
bool success = Dictionary.TryAdd(propertyName, value, out index);
#endif
if (success)
{
value?.AssignParent(this);
}
return success;
}

/// <summary>
/// Adds the specified property to the <see cref="JsonObject"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,46 @@ internal string GetPropertyName(JsonNode? node)
/// </summary>
/// <param name="propertyName">The name of the property to return.</param>
/// <param name="jsonNode">The JSON value of the property with the specified name.</param>
/// <exception cref="ArgumentNullException">
/// <paramref name="propertyName"/> is <see langword="null"/>.
/// </exception>
/// <returns>
/// <see langword="true"/> if a property with the specified name was found; otherwise, <see langword="false"/>.
/// </returns>
public bool TryGetPropertyValue(string propertyName, out JsonNode? jsonNode)
public bool TryGetPropertyValue(string propertyName, out JsonNode? jsonNode) => TryGetPropertyValue(propertyName, out jsonNode, out _);

/// <summary>
/// Gets the value associated with the specified property name.
/// </summary>
/// <param name="propertyName">The property name of the value to get.</param>
/// <param name="jsonNode">
/// When this method returns, it contains the value associated with the specified property name, if the property name is found;
/// otherwise <see langword="null"/>.
/// </param>
/// <param name="index">The index of <paramref name="propertyName"/> if found; otherwise, -1.</param>
/// <exception cref="ArgumentNullException">
/// <paramref name="propertyName"/> is <see langword="null"/>.
/// </exception>
/// <returns>
/// <see langword="true"/> if the <see cref="JsonObject"/> contains an element with the specified property name; otherwise, <see langword="false"/>.
/// </returns>
public bool TryGetPropertyValue(string propertyName, out JsonNode? jsonNode, out int index)
{
ArgumentNullException.ThrowIfNull(propertyName);

return Dictionary.TryGetValue(propertyName, out jsonNode);
#if NET9_0
index = Dictionary.IndexOf(propertyName);
if (index < 0)
{
jsonNode = null;
return false;
}

jsonNode = Dictionary.GetAt(index).Value;
return true;
#else
return Dictionary.TryGetValue(propertyName, out jsonNode, out index);
#endif
}

/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,9 @@ internal override void ReadElementAndSetProperty(
{
jObject[propertyName] = jNodeValue;
}
else
else if (!jObject.TryAdd(propertyName, jNodeValue))
{
// TODO: Use TryAdd once https://github.com/dotnet/runtime/issues/110244 is resolved.
if (jObject.ContainsKey(propertyName))
{
ThrowHelper.ThrowJsonException_DuplicatePropertyNotAllowed(propertyName);
}

jObject.Add(propertyName, jNodeValue);
ThrowHelper.ThrowJsonException_DuplicatePropertyNotAllowed(propertyName);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1604,6 +1604,69 @@ public static void JsonObject_IsIList()
Assert.Equal(-1, jObject.IndexOf("Two"));
}

[Fact]
public static void TryAdd_NewKey_EmptyJsonObject()
{
JsonObject jObject = new();

Assert.True(jObject.TryAdd("First", "value", out var index));
Assert.Equal(0, index);
}

[Fact]
public static void TryAdd_NewKey()
{
JsonObject jObject = new()
{
["One"] = 1,
["Two"] = "str",
["Three"] = null,
};

Assert.True(jObject.TryAdd("Four", 33, out var index));
Assert.Equal(3, index);
}

[Fact]
public static void TryAdd_ExistingKey()
{
JsonObject jObject = new()
{
["One"] = 1,
["Two"] = "str",
["Three"] = null,
};

Assert.False(jObject.TryAdd("Two", 33, out var index));
Assert.Equal(1, index);
}

[Fact]
public static void TryAdd_ThrowsArgumentNullException()
{
JsonObject jObject = new()
{
["One"] = 1,
["Two"] = "str",
["Three"] = null,
};

Assert.Throws<ArgumentNullException>(() => { jObject.TryAdd(null, 33); });
}

[Fact]
public static void TryGetPropertyValue_ThrowsArgumentNullException()
{
JsonObject jObject = new()
{
["One"] = 1,
["Two"] = "str",
["Three"] = null,
};

Assert.Throws<ArgumentNullException>(() => { jObject.TryGetPropertyValue(null, out var jsonNode, out var index); });
}

[Theory]
[InlineData(10_000)]
[InlineData(50_000)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,60 @@ public static void Parse_TryGetPropertyValue()
Assert.IsType<JsonObject>(node);
}

[Fact]
public static void Parse_TryGetPropertyValueWithIndex()
{
JsonObject jObject = JsonNode.Parse(JsonNodeTests.ExpectedDomJson).AsObject();

JsonNode? node;
int index;

Assert.True(jObject.TryGetPropertyValue("MyString", out node, out index));
Assert.Equal("Hello!", node.GetValue<string>());
Assert.Equal(0, index);

Assert.True(jObject.TryGetPropertyValue("MyNull", out node, out index));
Assert.Null(node);
Assert.Equal(1, index);

Assert.True(jObject.TryGetPropertyValue("MyBoolean", out node, out index));
Assert.False(node.GetValue<bool>());
Assert.Equal(2, index);

Assert.True(jObject.TryGetPropertyValue("MyArray", out node, out index));
Assert.IsType<JsonArray>(node);
Assert.Equal(3, index);

Assert.True(jObject.TryGetPropertyValue("MyInt", out node, out index));
Assert.Equal(43, node.GetValue<int>());
Assert.Equal(4, index);

Assert.True(jObject.TryGetPropertyValue("MyDateTime", out node, out index));
Assert.Equal("2020-07-08T00:00:00", node.GetValue<string>());
Assert.Equal(5, index);

Assert.True(jObject.TryGetPropertyValue("MyGuid", out node, out index));
Assert.Equal("ed957609-cdfe-412f-88c1-02daca1b4f51", node.AsValue().GetValue<Guid>().ToString());
Assert.Equal(6, index);

Assert.True(jObject.TryGetPropertyValue("MyObject", out node, out index));
Assert.IsType<JsonObject>(node);
Assert.Equal(7, index);
}

[Fact]
public static void Parse_TryGetPropertyValueFail()
{
JsonObject jObject = JsonNode.Parse(JsonNodeTests.ExpectedDomJson).AsObject();

JsonNode? node;
int index;

Assert.False(jObject.TryGetPropertyValue("NonExistentKey", out node, out index));
Assert.Null(node);
Assert.Equal(-1, index);
}

[Fact]
public static void Parse_TryGetValue()
{
Expand Down
Loading