* Do we want to add recursive equals on `JsonArray` and `JsonObject`?
* Do we want to make `JsonNode` derived types implement `IComparable` (which ones)?
* Do we want `JsonObject` to implement `IDictionary` and `JsonArray` to implement `IList` (currently JsonArray does, but JsonObject not)?
+* Do we want `JsonArray` to support `Contains`, `IndexOf` and `LastIndexOf` if we keep reference equality for `JsonArray`/`JsonObject` and don't have a good way of comparing numbers?
* Would escaped characters be supported for creating `JsonNumber` from string?
* Is the API for `JsonNode` and `JsonElement` interactions sufficient?
* Do we want to support duplicate and order preservation/control when adding/removing values in `JsonArray`/`JsonObject`?
| Solution | Pros | Cons |
|----------|:-------------|--------|
|current API| - no additional checks need to be made | - creating recursive loop by the user may be problematic |
- |tracking nodes | - handles recursive loop problem | - when node is added to a parent, it needs to be checked <br> if it already has a parent and make a copy if it has |
+ |tracking nodes | - handles recursive loop problem | - when node is added to a parent, it needs to be checked <br> if it already has a parent and make a copy if it has |
* Do we want to change `JsonNumber`'s backing field to something different than `string`?
Suggestions:
- `Span<byte>` or array of `Utf8String`/`Char8` (once they come online in the future) / `byte`
* Should `ToString` on `JsonBoolean` and `JsonString` return the .NET or JSON representation?
* Do we want to keep implicit cast operators (even though for `JsonNumber` it would mean throwing in some cases, which is against FDG)?
* Do we want overloads that take a `StringComparison` enum for methods retrieving properties? It would make API easier to use where case is not important.
-* Where do we want to enable user to set handling properties manner?
+* Where do we want to enable user to set handling properties manner? Do we want to support it as `JsonElement` does not?
+* Do we want to support transforming `JsonObject` into JSON string? Should `ToString` behave this way or do we want an additional method - e.g. `GetJsonString` (it might be confusing as we have a type `JsonString`) or `GetJsonRepresenation`?
+* `AsJsonElement` function on `JsonNode` currently does not work for `null` node. Do we want to support null case somehow?
+* Do we want both `Clone` and `DeepCopy` methods for `JsonNode`?
+* Are we OK with needing to cast `JsonArray` to `JsonNode` in order to add it to `JsonObject`? (there's an ambiguous call between `JsonArray` and `IEnumerable<JsonNode>` right now)
+* Do we want `IsImmutable` property for `JsonElement`?
## Useful links
Ignore = 1,
Error = 2,
}
+ public sealed partial class JsonArray : System.Text.Json.JsonNode, System.Collections.Generic.ICollection<System.Text.Json.JsonNode>, System.Collections.Generic.IEnumerable<System.Text.Json.JsonNode>, System.Collections.Generic.IList<System.Text.Json.JsonNode>, System.Collections.Generic.IReadOnlyCollection<System.Text.Json.JsonNode>, System.Collections.Generic.IReadOnlyList<System.Text.Json.JsonNode>, System.Collections.IEnumerable
+ {
+ public JsonArray() { }
+ public JsonArray(System.Collections.Generic.IEnumerable<bool> values) { }
+ public JsonArray(System.Collections.Generic.IEnumerable<byte> values) { }
+ public JsonArray(System.Collections.Generic.IEnumerable<decimal> values) { }
+ public JsonArray(System.Collections.Generic.IEnumerable<double> values) { }
+ public JsonArray(System.Collections.Generic.IEnumerable<short> values) { }
+ public JsonArray(System.Collections.Generic.IEnumerable<int> values) { }
+ public JsonArray(System.Collections.Generic.IEnumerable<long> values) { }
+ [System.CLSCompliantAttribute(false)]
+ public JsonArray(System.Collections.Generic.IEnumerable<sbyte> values) { }
+ public JsonArray(System.Collections.Generic.IEnumerable<float> values) { }
+ public JsonArray(System.Collections.Generic.IEnumerable<string> values) { }
+ public JsonArray(System.Collections.Generic.IEnumerable<System.Text.Json.JsonNode> values) { }
+ [System.CLSCompliantAttribute(false)]
+ public JsonArray(System.Collections.Generic.IEnumerable<ushort> values) { }
+ [System.CLSCompliantAttribute(false)]
+ public JsonArray(System.Collections.Generic.IEnumerable<uint> values) { }
+ [System.CLSCompliantAttribute(false)]
+ public JsonArray(System.Collections.Generic.IEnumerable<ulong> values) { }
+ public int Count { get { throw null; } }
+ public bool IsReadOnly { get { throw null; } }
+ public System.Text.Json.JsonNode this[int idx] { get { throw null; } set { } }
+ public override System.Text.Json.JsonValueKind ValueKind { get { throw null; } }
+ public void Add(bool value) { }
+ public void Add(byte value) { }
+ public void Add(decimal value) { }
+ public void Add(double value) { }
+ public void Add(short value) { }
+ public void Add(int value) { }
+ public void Add(long value) { }
+ [System.CLSCompliantAttribute(false)]
+ public void Add(sbyte value) { }
+ public void Add(float value) { }
+ public void Add(string value) { }
+ public void Add(System.Text.Json.JsonNode value) { }
+ [System.CLSCompliantAttribute(false)]
+ public void Add(ushort value) { }
+ [System.CLSCompliantAttribute(false)]
+ public void Add(uint value) { }
+ [System.CLSCompliantAttribute(false)]
+ public void Add(ulong value) { }
+ public void Clear() { }
+ public override System.Text.Json.JsonNode Clone() { throw null; }
+ public bool Contains(System.Text.Json.JsonNode value) { throw null; }
+ public System.Text.Json.JsonArrayEnumerator GetEnumerator() { throw null; }
+ public int IndexOf(System.Text.Json.JsonNode item) { throw null; }
+ public void Insert(int index, bool item) { }
+ public void Insert(int index, byte item) { }
+ public void Insert(int index, decimal item) { }
+ public void Insert(int index, double item) { }
+ public void Insert(int index, short item) { }
+ public void Insert(int index, int item) { }
+ public void Insert(int index, long item) { }
+ [System.CLSCompliantAttribute(false)]
+ public void Insert(int index, sbyte item) { }
+ public void Insert(int index, float item) { }
+ public void Insert(int index, string item) { }
+ public void Insert(int index, System.Text.Json.JsonNode item) { }
+ [System.CLSCompliantAttribute(false)]
+ public void Insert(int index, ushort item) { }
+ [System.CLSCompliantAttribute(false)]
+ public void Insert(int index, uint item) { }
+ [System.CLSCompliantAttribute(false)]
+ public void Insert(int index, ulong item) { }
+ public int LastIndexOf(System.Text.Json.JsonNode item) { throw null; }
+ public bool Remove(System.Text.Json.JsonNode item) { throw null; }
+ public int RemoveAll(System.Predicate<System.Text.Json.JsonNode> match) { throw null; }
+ public void RemoveAt(int index) { }
+ void System.Collections.Generic.ICollection<System.Text.Json.JsonNode>.CopyTo(System.Text.Json.JsonNode[] array, int arrayIndex) { }
+ System.Collections.Generic.IEnumerator<System.Text.Json.JsonNode> System.Collections.Generic.IEnumerable<System.Text.Json.JsonNode>.GetEnumerator() { throw null; }
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
+ }
+ public partial struct JsonArrayEnumerator : System.Collections.Generic.IEnumerator<System.Text.Json.JsonNode>, System.Collections.IEnumerator, System.IDisposable
+ {
+ private object _dummy;
+ public JsonArrayEnumerator(System.Text.Json.JsonArray jsonArray) { throw null; }
+ public System.Text.Json.JsonNode Current { get { throw null; } }
+ object System.Collections.IEnumerator.Current { get { throw null; } }
+ public void Dispose() { }
+ public bool MoveNext() { throw null; }
+ void System.Collections.IEnumerator.Reset() { }
+ }
public sealed partial class JsonBoolean : System.Text.Json.JsonNode, System.IEquatable<System.Text.Json.JsonBoolean>
{
public JsonBoolean() { }
public JsonBoolean(bool value) { }
public bool Value { get { throw null; } set { } }
+ public override System.Text.Json.JsonValueKind ValueKind { get { throw null; } }
+ public override System.Text.Json.JsonNode Clone() { throw null; }
public override bool Equals(object obj) { throw null; }
public bool Equals(System.Text.Json.JsonBoolean other) { throw null; }
public override int GetHashCode() { throw null; }
{
private readonly object _dummy;
private readonly int _dummyPrimitive;
+ public bool IsImmutable { get { throw null; } }
public System.Text.Json.JsonElement this[int index] { get { throw null; } }
public System.Text.Json.JsonValueKind ValueKind { get { throw null; } }
public System.Text.Json.JsonElement Clone() { throw null; }
public abstract partial class JsonNode
{
internal JsonNode() { }
+ public abstract System.Text.Json.JsonValueKind ValueKind { get; }
+ public System.Text.Json.JsonElement AsJsonElement() { throw null; }
+ public abstract System.Text.Json.JsonNode Clone();
+ public static System.Text.Json.JsonNode DeepCopy(System.Text.Json.JsonElement jsonElement) { throw null; }
+ public static System.Text.Json.JsonNode GetNode(System.Text.Json.JsonElement jsonElement) { throw null; }
+ public static System.Text.Json.JsonNode Parse(string json) { throw null; }
+ public static bool TryGetNode(System.Text.Json.JsonElement jsonElement, out System.Text.Json.JsonNode jsonNode) { throw null; }
}
public sealed partial class JsonNumber : System.Text.Json.JsonNode, System.IEquatable<System.Text.Json.JsonNumber>
{
public JsonNumber(uint value) { }
[System.CLSCompliantAttribute(false)]
public JsonNumber(ulong value) { }
+ public override System.Text.Json.JsonValueKind ValueKind { get { throw null; } }
+ public override System.Text.Json.JsonNode Clone() { throw null; }
public override bool Equals(object obj) { throw null; }
public bool Equals(System.Text.Json.JsonNumber other) { throw null; }
public byte GetByte() { throw null; }
public System.Text.Json.JsonNode this[string propertyName] { get { throw null; } set { } }
public System.Collections.Generic.ICollection<string> PropertyNames { get { throw null; } }
public System.Collections.Generic.ICollection<System.Text.Json.JsonNode> PropertyValues { get { throw null; } }
+ public override System.Text.Json.JsonValueKind ValueKind { get { throw null; } }
public void Add(System.Collections.Generic.KeyValuePair<string, System.Text.Json.JsonNode> jsonProperty) { }
public void Add(string propertyName, bool propertyValue) { }
public void Add(string propertyName, byte propertyValue) { }
+ public void Add(string propertyName, System.Collections.Generic.IEnumerable<System.Text.Json.JsonNode> propertyValues) { }
public void Add(string propertyName, System.DateTime propertyValue) { }
public void Add(string propertyName, System.DateTimeOffset propertyValue) { }
public void Add(string propertyName, decimal propertyValue) { }
[System.CLSCompliantAttribute(false)]
public void Add(string propertyName, ulong propertyValue) { }
public void AddRange(System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, System.Text.Json.JsonNode>> jsonProperties) { }
+ public override System.Text.Json.JsonNode Clone() { throw null; }
public bool ContainsProperty(string propertyName) { throw null; }
- public System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<string, System.Text.Json.JsonNode>> GetEnumerator() { throw null; }
+ public System.Text.Json.JsonObjectEnumerator GetEnumerator() { throw null; }
+ public System.Text.Json.JsonArray GetJsonArrayPropertyValue(string propertyName) { throw null; }
public System.Text.Json.JsonObject GetJsonObjectPropertyValue(string propertyName) { throw null; }
public System.Text.Json.JsonNode GetPropertyValue(string propertyName) { throw null; }
public bool Remove(string propertyName) { throw null; }
+ System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<string, System.Text.Json.JsonNode>> System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<System.String,System.Text.Json.JsonNode>>.GetEnumerator() { throw null; }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
+ public bool TryGetJsonArrayPropertyValue(string propertyName, out System.Text.Json.JsonArray jsonArray) { throw null; }
public bool TryGetJsonObjectPropertyValue(string propertyName, out System.Text.Json.JsonObject jsonObject) { throw null; }
public bool TryGetPropertyValue(string propertyName, out System.Text.Json.JsonNode jsonNode) { throw null; }
}
object System.Collections.IEnumerator.Current { get { throw null; } }
public void Dispose() { }
public bool MoveNext() { throw null; }
- public void Reset() { }
+ void System.Collections.IEnumerator.Reset() { }
}
public readonly partial struct JsonProperty
{
public JsonString(System.ReadOnlySpan<char> value) { }
public JsonString(string value) { }
public string Value { get { throw null; } set { } }
+ public override System.Text.Json.JsonValueKind ValueKind { get { throw null; } }
+ public override System.Text.Json.JsonNode Clone() { throw null; }
public override bool Equals(object obj) { throw null; }
public bool Equals(System.Text.Json.JsonString other) { throw null; }
+ public System.DateTime GetDateTime() { throw null; }
+ public System.DateTimeOffset GetDateTimeOffset() { throw null; }
+ public System.Guid GetGuid() { throw null; }
public override int GetHashCode() { throw null; }
public static bool operator ==(System.Text.Json.JsonString left, System.Text.Json.JsonString right) { throw null; }
public static implicit operator System.Text.Json.JsonString (string value) { throw null; }
public static bool operator !=(System.Text.Json.JsonString left, System.Text.Json.JsonString right) { throw null; }
public override string ToString() { throw null; }
+ public bool TryGetDateTime(out System.DateTime value) { throw null; }
+ public bool TryGetDateTimeOffset(out System.DateTimeOffset value) { throw null; }
+ public bool TryGetGuid(out System.Guid value) { throw null; }
}
public enum JsonTokenType : byte
{
<data name="InvalidDuplicatePropertyNameHandling" xml:space="preserve">
<value>The DuplicatePropertyNameHandling enum must be set to one of the supported values.</value>
</data>
+ <data name="ArrayModifiedDuringIteration" xml:space="preserve">
+ <value>The JSON array was modified during iteration.</value>
+ </data>
</root>
\ No newline at end of file
</ItemGroup>
<ItemGroup>
<Compile Include="System\Text\Json\Node\DuplicatePropertyNameHandling.cs" />
+ <Compile Include="System\Text\Json\Node\JsonArray.cs" />
+ <Compile Include="System\Text\Json\Node\JsonArrayEnumerator.cs" />
<Compile Include="System\Text\Json\Node\JsonBoolean.cs" />
<Compile Include="System\Text\Json\Node\JsonNode.cs" />
<Compile Include="System\Text\Json\Node\JsonNumber.cs" />
{
private readonly JsonElement _target;
private int _curIdx;
- private readonly int _endIdx;
+ private readonly int _endIdxOrVersion;
internal ArrayEnumerator(JsonElement target)
{
- Debug.Assert(target.TokenType == JsonTokenType.StartArray);
-
_target = target;
_curIdx = -1;
- _endIdx = _target._parent.GetEndIndex(_target._idx, includeEndElement: false);
+
+ if (target._parent is JsonDocument document)
+ {
+ Debug.Assert(target.TokenType == JsonTokenType.StartArray);
+
+ _endIdxOrVersion = document.GetEndIndex(_target._idx, includeEndElement: false);
+ }
+ else
+ {
+ var jsonArray = (JsonArray)target._parent;
+
+ _endIdxOrVersion = jsonArray._version;
+ }
}
/// <inheritdoc />
- public JsonElement Current =>
- _curIdx < 0 ? default : new JsonElement(_target._parent, _curIdx);
+ public JsonElement Current
+ {
+ get
+ {
+ if (_curIdx < 0)
+ {
+ return default;
+ }
+
+ if (_target._parent is JsonArray jsonArray)
+ {
+ if (_curIdx >= jsonArray.Count)
+ {
+ return default;
+ }
+
+ return jsonArray[_curIdx].AsJsonElement();
+ }
+
+ var document = (JsonDocument)_target._parent;
+ return new JsonElement(document, _curIdx);
+ }
+ }
/// <summary>
/// Returns an enumerator that iterates through a collection.
/// <inheritdoc />
public void Dispose()
{
- _curIdx = _endIdx;
+ _curIdx = _endIdxOrVersion;
}
/// <inheritdoc />
/// <inheritdoc />
public bool MoveNext()
{
- if (_curIdx >= _endIdx)
+ if (_target._parent is JsonArray jsonArray)
+ {
+ if (jsonArray._version != _endIdxOrVersion)
+ {
+ throw new InvalidOperationException(SR.ArrayModifiedDuringIteration);
+ }
+
+ if (_curIdx >= jsonArray.Count)
+ {
+ return false;
+ }
+
+ _curIdx++;
+ return _curIdx < jsonArray.Count;
+ }
+
+ if (_curIdx >= _endIdxOrVersion)
{
return false;
}
}
else
{
- _curIdx = _target._parent.GetEndIndex(_curIdx, includeEndElement: true);
+ var document = (JsonDocument)_target._parent;
+ _curIdx = document.GetEndIndex(_curIdx, includeEndElement: true);
}
- return _curIdx < _endIdx;
+ return _curIdx < _endIdxOrVersion;
}
}
}
private readonly JsonElement _target;
private int _curIdx;
private readonly int _endIdx;
+ private JsonObjectEnumerator _jsonObjectEnumerator;
internal ObjectEnumerator(JsonElement target)
{
- Debug.Assert(target.TokenType == JsonTokenType.StartObject);
-
_target = target;
_curIdx = -1;
- _endIdx = _target._parent.GetEndIndex(_target._idx, includeEndElement: false);
+
+ if (target._parent is JsonDocument document)
+ {
+ Debug.Assert(target.TokenType == JsonTokenType.StartObject);
+
+ _endIdx = document.GetEndIndex(_target._idx, includeEndElement: false);
+ _jsonObjectEnumerator = default;
+ }
+ else
+ {
+ _endIdx = -1;
+
+ var jsonObject = (JsonObject)target._parent;
+ _jsonObjectEnumerator = new JsonObjectEnumerator(jsonObject);
+ }
}
/// <inheritdoc />
- public JsonProperty Current =>
- _curIdx < 0 ?
- default :
- new JsonProperty(new JsonElement(_target._parent, _curIdx));
+ public JsonProperty Current
+ {
+ get
+ {
+ if (!_target.IsImmutable)
+ {
+ KeyValuePair<string, JsonNode> propertyPair = _jsonObjectEnumerator.Current;
+
+ // propertyPair.Key is null before first after last call of MoveNext
+ if (propertyPair.Key == null)
+ {
+ return default;
+ }
+
+ // null JsonNode case
+ if (propertyPair.Value == null)
+ {
+ return new JsonProperty(new JsonElement(null), propertyPair.Key);
+ }
+
+ return new JsonProperty(propertyPair.Value.AsJsonElement(), propertyPair.Key);
+ }
+
+ if (_curIdx < 0)
+ {
+ return default;
+ }
+
+ var document = (JsonDocument)_target._parent;
+ return new JsonProperty(new JsonElement(document, _curIdx));
+ }
+ }
/// <summary>
/// Returns an enumerator that iterates the properties of an object.
public void Dispose()
{
_curIdx = _endIdx;
+ if (!_target.IsImmutable)
+ {
+ _jsonObjectEnumerator.Dispose();
+ }
}
/// <inheritdoc />
public void Reset()
{
_curIdx = -1;
+ if (!_target.IsImmutable)
+ {
+ ((IEnumerator)_jsonObjectEnumerator).Reset();
+ }
}
/// <inheritdoc />
/// <inheritdoc />
public bool MoveNext()
{
+ if (!_target.IsImmutable)
+ {
+ return _jsonObjectEnumerator.MoveNext();
+ }
+
if (_curIdx >= _endIdx)
{
return false;
}
else
{
- _curIdx = _target._parent.GetEndIndex(_curIdx, includeEndElement: true);
+ var document = (JsonDocument)_target._parent;
+ _curIdx = document.GetEndIndex(_curIdx, includeEndElement: true);
}
// _curIdx is now pointing at a property name, move one more to get the value
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public readonly partial struct JsonElement
{
- private readonly JsonDocument _parent;
+ internal readonly object _parent;
private readonly int _idx;
internal JsonElement(JsonDocument parent, int idx)
_idx = idx;
}
- private JsonTokenType TokenType => _parent?.GetJsonTokenType(_idx) ?? JsonTokenType.None;
+ internal JsonElement(JsonNode parent)
+ {
+ _parent = parent;
+ _idx = -1;
+ }
+
+ /// <summary>
+ /// Indicates whether or not this instance is immutable.
+ /// </summary>
+ public bool IsImmutable => _idx != -1;
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private JsonTokenType TokenType
+ {
+ get
+ {
+ JsonDocument document = (JsonDocument)_parent;
+ return document?.GetJsonTokenType(_idx) ?? JsonTokenType.None;
+ }
+ }
/// <summary>
/// The <see cref="JsonValueKind"/> that the value is.
/// </summary>
/// <exception cref="ObjectDisposedException">
/// The parent <see cref="JsonDocument"/> has been disposed.
/// </exception>
- public JsonValueKind ValueKind => TokenType.ToValueKind();
+ public JsonValueKind ValueKind
+ {
+ get
+ {
+ if (IsImmutable)
+ {
+ return TokenType.ToValueKind();
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode == null)
+ {
+ return JsonValueKind.Null;
+ }
+
+ return jsonNode.ValueKind;
+ }
+ }
/// <summary>
/// Get the value at a specified index when the current value is a
{
CheckValidInstance();
- return _parent.GetArrayIndexElement(_idx, index);
+ if (_parent is JsonDocument document)
+ {
+ return document.GetArrayIndexElement(_idx, index);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonArray jsonArray)
+ {
+ return jsonArray[index].AsJsonElement();
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Array, jsonNode.ValueKind);
}
}
{
CheckValidInstance();
- return _parent.GetArrayLength(_idx);
+ if (_parent is JsonDocument document)
+ {
+ return document.GetArrayLength(_idx);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonArray jsonArray)
+ {
+ return jsonArray.Count;
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Array, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetNamedPropertyValue(_idx, propertyName, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetNamedPropertyValue(_idx, propertyName, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonObject jsonObject)
+ {
+ if (jsonObject.TryGetPropertyValue(propertyName.ToString(), out JsonNode nodeValue))
+ {
+ value = nodeValue.AsJsonElement();
+ return true;
+ }
+
+ value = default;
+ return false;
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Object, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetNamedPropertyValue(_idx, utf8PropertyName, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetNamedPropertyValue(_idx, utf8PropertyName, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonObject jsonObject)
+ {
+ if (jsonObject.TryGetPropertyValue(JsonHelpers.Utf8GetString(utf8PropertyName), out JsonNode nodeValue))
+ {
+ value = nodeValue.AsJsonElement();
+ return true;
+ }
+
+ value = default;
+ return false;
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Object, jsonNode.ValueKind);
}
/// <summary>
/// </exception>
public bool GetBoolean()
{
- // CheckValidInstance is redundant. Asking for the type will
- // return None, which then throws the same exception in the return statement.
+ CheckValidInstance();
+
+ if (_parent is JsonDocument document)
+ {
+ JsonTokenType type = TokenType;
- JsonTokenType type = TokenType;
+ return
+ type == JsonTokenType.True ? true :
+ type == JsonTokenType.False ? false :
+ throw ThrowHelper.GetJsonElementWrongTypeException(nameof(Boolean), type);
+ }
+
+ var jsonNode = (JsonNode)_parent;
- return
- type == JsonTokenType.True ? true :
- type == JsonTokenType.False ? false :
- throw ThrowHelper.GetJsonElementWrongTypeException(nameof(Boolean), type);
+ if (_parent is JsonBoolean jsonBoolean)
+ {
+ return jsonBoolean.Value;
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(nameof(Boolean), jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.GetString(_idx, JsonTokenType.String);
+ if (_parent is JsonDocument document)
+ {
+ return document.GetString(_idx, JsonTokenType.String);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonString jsonString)
+ {
+ return jsonString.Value;
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.String, jsonNode.ValueKind);
}
/// <summary>
/// </summary>
/// <param name="value">Receives the value.</param>
/// <remarks>
- /// This method does not create a byte[] representation of values other than bsae 64 encoded JSON strings.
+ /// This method does not create a byte[] representation of values other than base 64 encoded JSON strings.
/// </remarks>
/// <returns>
/// <see langword="true"/> if the entire token value is encoded as valid Base64 text and can be successfully decoded to bytes.
{
CheckValidInstance();
- return _parent.TryGetValue(_idx, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetValue(_idx, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonString)
+ {
+ throw new NotSupportedException();
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.String, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetValue(_idx, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetValue(_idx, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonNumber jsonNumber)
+ {
+ return jsonNumber.TryGetSByte(out value);
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetValue(_idx, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetValue(_idx, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonNumber jsonNumber)
+ {
+ return jsonNumber.TryGetByte(out value);
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetValue(_idx, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetValue(_idx, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (_parent is JsonNumber jsonNumber)
+ {
+ return jsonNumber.TryGetInt16(out value);
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetValue(_idx, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetValue(_idx, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (_parent is JsonNumber jsonNumber)
+ {
+ return jsonNumber.TryGetUInt16(out value);
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetValue(_idx, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetValue(_idx, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonNumber jsonNumber)
+ {
+ return jsonNumber.TryGetInt32(out value);
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetValue(_idx, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetValue(_idx, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonNumber jsonNumber)
+ {
+ return jsonNumber.TryGetUInt32(out value);
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetValue(_idx, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetValue(_idx, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonNumber jsonNumber)
+ {
+ return jsonNumber.TryGetInt64(out value);
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetValue(_idx, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetValue(_idx, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonNumber jsonNumber)
+ {
+ return jsonNumber.TryGetUInt64(out value);
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetValue(_idx, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetValue(_idx, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (_parent is JsonNumber jsonNumber)
+ {
+ return jsonNumber.TryGetDouble(out value);
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetValue(_idx, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetValue(_idx, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonNumber jsonNumber)
+ {
+ return jsonNumber.TryGetSingle(out value);
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetValue(_idx, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetValue(_idx, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonNumber jsonNumber)
+ {
+ return jsonNumber.TryGetDecimal(out value);
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetValue(_idx, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetValue(_idx, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonString jsonString)
+ {
+ return jsonString.TryGetDateTime(out value);
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.String, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetValue(_idx, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetValue(_idx, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonString jsonString)
+ {
+ return jsonString.TryGetDateTimeOffset(out value);
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.String, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TryGetValue(_idx, out value);
+ if (_parent is JsonDocument document)
+ {
+ return document.TryGetValue(_idx, out value);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+
+ if (jsonNode is JsonString jsonString)
+ {
+ return jsonString.TryGetGuid(out value);
+ }
+
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.String, jsonNode.ValueKind);
}
/// <summary>
{
CheckValidInstance();
- return _parent.GetNameOfPropertyValue(_idx);
+ var document = (JsonDocument)_parent;
+ return document.GetNameOfPropertyValue(_idx);
}
/// <summary>
{
CheckValidInstance();
- return _parent.GetRawValueAsString(_idx);
+ if (_parent is JsonDocument document)
+ {
+ return document.GetRawValueAsString(_idx);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+ return jsonNode.ToString();
}
internal string GetPropertyRawText()
{
CheckValidInstance();
- return _parent.GetPropertyRawValueAsString(_idx);
+ var document = (JsonDocument)_parent;
+ return document.GetPropertyRawValueAsString(_idx);
}
/// <summary>
{
CheckValidInstance();
- return _parent.TextEquals(_idx, utf8Text, isPropertyName);
+ var document = (JsonDocument)_parent;
+ return document.TextEquals(_idx, utf8Text, isPropertyName);
}
internal bool TextEqualsHelper(ReadOnlySpan<char> text, bool isPropertyName)
{
CheckValidInstance();
- return _parent.TextEquals(_idx, text, isPropertyName);
+ var document = (JsonDocument)_parent;
+ return document.TextEquals(_idx, text, isPropertyName);
}
/// <summary>
CheckValidInstance();
- _parent.WriteElementTo(_idx, writer);
+ var document = (JsonDocument)_parent;
+ document.WriteElementTo(_idx, writer);
}
/// <summary>
{
CheckValidInstance();
- JsonTokenType tokenType = TokenType;
+ if (_parent is JsonDocument)
+ {
+ JsonTokenType tokenType = TokenType;
- if (tokenType != JsonTokenType.StartArray)
+ if (tokenType != JsonTokenType.StartArray)
+ {
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonTokenType.StartArray, tokenType);
+ }
+ }
+ else if (_parent is JsonNode node)
{
- throw ThrowHelper.GetJsonElementWrongTypeException(JsonTokenType.StartArray, tokenType);
+ if (node.ValueKind != JsonValueKind.Array)
+ {
+ throw new InvalidOperationException();
+ }
}
return new ArrayEnumerator(this);
{
CheckValidInstance();
- JsonTokenType tokenType = TokenType;
+ if (_parent is JsonDocument)
+ {
+ JsonTokenType tokenType = TokenType;
- if (tokenType != JsonTokenType.StartObject)
+ if (tokenType != JsonTokenType.StartObject)
+ {
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonTokenType.StartObject, tokenType);
+ }
+ }
+ else if (_parent is JsonNode node)
{
- throw ThrowHelper.GetJsonElementWrongTypeException(JsonTokenType.StartObject, tokenType);
+ if (node.ValueKind != JsonValueKind.Object)
+ {
+ throw new InvalidOperationException();
+ }
}
return new ObjectEnumerator(this);
{
// null parent should have hit the None case
Debug.Assert(_parent != null);
- return _parent.GetRawValueAsString(_idx);
+ if (_parent is JsonDocument document)
+ {
+ return document.GetRawValueAsString(_idx);
+ }
+
+ var jsonNode = (JsonNode)_parent;
+ return jsonNode.ToString();
}
case JsonTokenType.String:
return GetString();
{
CheckValidInstance();
- if (!_parent.IsDisposable)
+ if (_parent is JsonDocument document)
{
- return this;
+ if (!document.IsDisposable)
+ {
+ return this;
+ }
+
+ return document.CloneElement(_idx);
}
- return _parent.CloneElement(_idx);
+ var jsonNode = (JsonNode)_parent;
+ return jsonNode.Clone().AsJsonElement();
}
private void CheckValidInstance()
{
throw new InvalidOperationException();
}
+
+ Debug.Assert(_parent is JsonDocument || _parent is JsonNode);
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
/// The value of this property.
/// </summary>
public JsonElement Value { get; }
+ private string _name { get; }
- internal JsonProperty(JsonElement value)
+ internal JsonProperty(JsonElement value, string name = null)
{
Value = value;
+ _name = name;
}
/// <summary>
/// The name of this property.
/// </summary>
- public string Name => Value.GetPropertyName();
+ public string Name => _name ?? Value.GetPropertyName();
/// <summary>
/// Compares <paramref name="text" /> to the name of this property.
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections;
+using System.Collections.Generic;
+
+namespace System.Text.Json
+{
+ /// <summary>
+ /// Represents a JSON array.
+ /// </summary>
+ public sealed class JsonArray : JsonNode, IList<JsonNode>, IReadOnlyList<JsonNode>
+ {
+ internal readonly List<JsonNode> _list;
+ internal int _version;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArray"/> class representing the empty array.
+ /// </summary>
+ public JsonArray()
+ {
+ _list = new List<JsonNode>();
+ _version = 0;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArray"/> class representing the specified collection.
+ /// </summary>
+ /// <param name="values">Collection to represent.</param>
+ public JsonArray(IEnumerable<JsonNode> values)
+ {
+ _list = new List<JsonNode>(values);
+ _version = 0;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArray"/> class representing the specified collection of <see cref="string"/>s.
+ /// </summary>
+ /// <param name="values">Collection to represent.</param>
+ /// <exception cref="ArgumentNullException">
+ /// Some of provided values are null.
+ /// </exception>
+ public JsonArray(IEnumerable<string> values) : this()
+ {
+ foreach (string value in values)
+ {
+ if (value == null)
+ {
+ _list.Add(null);
+ }
+
+ _list.Add(new JsonString(value));
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArray"/> class representing the specified collection of <see cref="byte"/>s.
+ /// </summary>
+ /// <param name="values">Collection to represent.</param>
+ public JsonArray(IEnumerable<bool> values) : this()
+ {
+ foreach (bool value in values)
+ {
+ _list.Add(new JsonBoolean(value));
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArray"/> class representing the specified collection of <see cref="byte"/>s.
+ /// </summary>
+ /// <param name="values">Collection to represent.</param>
+ public JsonArray(IEnumerable<byte> values) : this()
+ {
+ foreach (byte value in values)
+ {
+ _list.Add(new JsonNumber(value));
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArray"/> class representing the specified collection of <see cref="short"/>s.
+ /// </summary>
+ /// <param name="values">Collection to represent.</param>
+ public JsonArray(IEnumerable<short> values) : this()
+ {
+ foreach (short value in values)
+ {
+ _list.Add(new JsonNumber(value));
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArray"/> class representing the specified collection of <see cref="int"/>s.
+ /// </summary>
+ /// <param name="values">Collection to represent.</param>
+ public JsonArray(IEnumerable<int> values) : this()
+ {
+ foreach (int value in values)
+ {
+ _list.Add(new JsonNumber(value));
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArray"/> class representing the specified collection of <see cref="long"/>s.
+ /// </summary>
+ /// <param name="values">Collection to represent.</param>
+ public JsonArray(IEnumerable<long> values) : this()
+ {
+ foreach (long value in values)
+ {
+ _list.Add(new JsonNumber(value));
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArray"/> class representing the specified collection of <see cref="float"/>s.
+ /// </summary>
+ /// <param name="values">Collection to represent.</param>
+ /// <exception cref="ArgumentException">
+ /// Some of provided values are not in a legal JSON number format.
+ /// </exception>
+ public JsonArray(IEnumerable<float> values) : this()
+ {
+ foreach (float value in values)
+ {
+ _list.Add(new JsonNumber(value));
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArray"/> class representing the specified collection of <see cref="double"/>s.
+ /// </summary>
+ /// <param name="values">Collection to represent.</param>
+ /// <exception cref="ArgumentException">
+ /// Some of provided values are not in a legal JSON number format.
+ /// </exception>
+ public JsonArray(IEnumerable<double> values) : this()
+ {
+ foreach (double value in values)
+ {
+ _list.Add(new JsonNumber(value));
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArray"/> class representing the specified collection of <see cref="sbyte"/>s.
+ /// </summary>
+ /// <param name="values">Collection to represent.</param>
+ [CLSCompliant(false)]
+ public JsonArray(IEnumerable<sbyte> values) : this()
+ {
+ foreach (sbyte value in values)
+ {
+ _list.Add(new JsonNumber(value));
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArray"/> class representing the specified collection of <see cref="ushort"/>s.
+ /// </summary>
+ /// <param name="values">Collection to represent.</param>
+ [CLSCompliant(false)]
+ public JsonArray(IEnumerable<ushort> values) : this()
+ {
+ foreach (ushort value in values)
+ {
+ _list.Add(new JsonNumber(value));
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArray"/> class representing the specified collection of <see cref="uint"/>s.
+ /// </summary>
+ /// <param name="values">Collection to represent.</param>
+ [CLSCompliant(false)]
+ public JsonArray(IEnumerable<uint> values) : this()
+ {
+ foreach (uint value in values)
+ {
+ _list.Add(new JsonNumber(value));
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArray"/> class representing the specified collection of <see cref="ulong"/>s.
+ /// </summary>
+ /// <param name="values">Collection to represent.</param>
+ [CLSCompliant(false)]
+ public JsonArray(IEnumerable<ulong> values) : this()
+ {
+ foreach (ulong value in values)
+ {
+ _list.Add(new JsonNumber(value));
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArray"/> class representing the specified collection of <see cref="decimal"/>s.
+ /// </summary>
+ /// <param name="values">Collection to represent.</param>
+ public JsonArray(IEnumerable<decimal> values) : this()
+ {
+ foreach (decimal value in values)
+ {
+ _list.Add(new JsonNumber(value));
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the element at the specified index.
+ /// </summary>
+ /// <param name="idx">The zero-based index of the element to get or set.</param>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// Index is less than 0.
+ /// </exception>
+ public JsonNode this[int idx]
+ {
+ get => _list[idx];
+ set
+ {
+ _list[idx] = value;
+ _version++;
+ }
+ }
+
+ /// <summary>
+ /// Adds the specified <see cref="JsonNode"/> value as the last item in this collection.
+ /// </summary>
+ /// <param name="value">The value to add.</param>
+ /// <remarks>Null value is allowed and represents the JSON null value.</remarks>
+ public void Add(JsonNode value)
+ {
+ _list.Add(value);
+ _version++;
+ }
+
+ /// <summary>
+ /// Adds the specified value as a <see cref="JsonString"/> as the last item in this collection.
+ /// </summary>
+ /// <param name="value">The value to add.</param>
+ /// <remarks>Null value is allowed and represents the JSON null value.</remarks>
+ public void Add(string value)
+ {
+ if (value == null)
+ {
+ Add((JsonNode)null);
+ }
+ else
+ {
+ Add(new JsonString(value));
+ }
+ }
+
+ /// <summary>
+ /// Adds the specified value as a <see cref="JsonBoolean"/> as the last item in this collection.
+ /// </summary>
+ /// <param name="value">The value to add.</param>
+ public void Add(bool value) => Add(new JsonBoolean(value));
+
+ /// <summary>
+ /// Adds the specified value as a <see cref="JsonNumber"/> as the last item in this collection.
+ /// </summary>
+ /// <param name="value">The value to add.</param>
+ public void Add(byte value) => Add(new JsonNumber(value));
+
+ /// <summary>
+ /// Adds the specified value as a <see cref="JsonNumber"/> as the last item in this collection.
+ /// </summary>
+ /// <param name="value">The value to add.</param>
+ public void Add(short value) => Add(new JsonNumber(value));
+
+ /// <summary>
+ /// Adds the specified value as a <see cref="JsonNumber"/> as the last item in this collection.
+ /// </summary>
+ /// <param name="value">The value to add.</param>
+ public void Add(int value) => Add(new JsonNumber(value));
+
+ /// <summary>
+ /// Adds the specified value as a <see cref="JsonNumber"/> as the last item in this collection.
+ /// </summary>
+ /// <param name="value">The value to add.</param>
+ public void Add(long value) => Add(new JsonNumber(value));
+
+ /// <summary>
+ /// Adds the specified value as a <see cref="JsonNumber"/> as the last item in this collection.
+ /// </summary>
+ /// <param name="value">The value to add.</param>
+ /// <exception cref="ArgumentException">
+ /// Provided value is not in a legal JSON number format.
+ /// </exception>
+ public void Add(float value) => Add(new JsonNumber(value));
+
+ /// <summary>
+ /// Adds the specified value as a <see cref="JsonNumber"/> as the last item in this collection.
+ /// </summary>
+ /// <param name="value">The value to add.</param>
+ /// <exception cref="ArgumentException">
+ /// Provided value is not in a legal JSON number format.
+ /// </exception>
+ public void Add(double value) => Add(new JsonNumber(value));
+
+ /// <summary>
+ /// Adds the specified value as a <see cref="JsonNumber"/> as the last item in this collection.
+ /// </summary>
+ /// <param name="value">The value to add.</param>
+ [CLSCompliant(false)]
+ public void Add(sbyte value) => Add(new JsonNumber(value));
+
+ /// <summary>
+ /// Adds the specified value as a <see cref="JsonNumber"/> as the last item in this collection.
+ /// </summary>
+ /// <param name="value">The value to add.</param>
+ [CLSCompliant(false)]
+ public void Add(ushort value) => Add(new JsonNumber(value));
+
+ /// <summary>
+ /// Adds the specified value as a <see cref="JsonNumber"/> as the last item in this collection.
+ /// </summary>
+ /// <param name="value">The value to add.</param>
+ [CLSCompliant(false)]
+ public void Add(uint value) => Add(new JsonNumber(value));
+
+ /// <summary>
+ /// Adds the specified value as a <see cref="JsonNumber"/> as the last item in this collection.
+ /// </summary>
+ /// <param name="value">The value to add.</param>
+ [CLSCompliant(false)]
+ public void Add(ulong value) => Add(new JsonNumber(value));
+
+ /// <summary>
+ /// Adds the specified value as a <see cref="JsonNumber"/> as the last item in this collection.
+ /// </summary>
+ /// <param name="value">The value to add.</param>
+ public void Add(decimal value) => Add(new JsonNumber(value));
+
+ /// <summary>
+ /// Inserts the specified item at the specified index of the JSON array.
+ /// </summary>
+ /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
+ /// <param name="item">The item to add.</param>
+ /// <remarks>The <paramref name="item"/> parameter may be <see langword="null" />, which represents the JSON null value.</remarks>
+ public void Insert(int index, JsonNode item)
+ {
+ _list.Insert(index, item);
+ _version++;
+ }
+
+ /// <summary>
+ /// Inserts the specified item as a <see cref="JsonString"/> at the specified index of the collection.
+ /// </summary>
+ /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
+ /// <param name="item">The item to add.</param>
+ public void Insert(int index, string item) => Insert(index, new JsonString(item));
+
+ /// <summary>
+ /// Inserts the specified item as a <see cref="JsonBoolean"/> at the specified index of the collection.
+ /// </summary>
+ /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
+ /// <param name="item">The item to add.</param>
+ public void Insert(int index, bool item) => Insert(index, new JsonBoolean(item));
+
+ /// <summary>
+ /// Inserts the specified item as a <see cref="JsonNumber"/> at the specified index of the collection.
+ /// </summary>
+ /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
+ /// <param name="item">The item to add.</param>
+ public void Insert(int index, byte item) => Insert(index, new JsonNumber(item));
+
+ /// <summary>
+ /// Inserts the specified item as a <see cref="JsonNumber"/> at the specified index of the collection.
+ /// </summary>
+ /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
+ /// <param name="item">The item to add.</param>
+ public void Insert(int index, short item) => Insert(index, new JsonNumber(item));
+
+ /// <summary>
+ /// Inserts the specified item as a <see cref="JsonNumber"/> at the specified index of the collection.
+ /// </summary>
+ /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
+ /// <param name="item">The item to add.</param>
+ public void Insert(int index, int item) => Insert(index, new JsonNumber(item));
+
+ /// <summary>
+ /// Inserts the specified item as a <see cref="JsonNumber"/> at the specified index of the collection.
+ /// </summary>
+ /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
+ /// <param name="item">The item to add.</param>
+ public void Insert(int index, long item) => Insert(index, new JsonNumber(item));
+
+ /// <summary>
+ /// Inserts the specified item as a <see cref="JsonNumber"/> at the specified index of the collection.
+ /// </summary>
+ /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
+ /// <param name="item">The item to add.</param>
+ /// <exception cref="ArgumentException">
+ /// Provided value is not in a legal JSON number format.
+ /// </exception>
+ public void Insert(int index, float item) => Insert(index, new JsonNumber(item));
+
+ /// <summary>
+ /// Inserts the specified item as a <see cref="JsonNumber"/> at the specified index of the collection.
+ /// </summary>
+ /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
+ /// <param name="item">The item to add.</param>
+ /// <exception cref="ArgumentException">
+ /// Provided value is not in a legal JSON number format.
+ /// </exception>
+ public void Insert(int index, double item) => Insert(index, new JsonNumber(item));
+
+ /// <summary>
+ /// Inserts the specified item as a <see cref="JsonNumber"/> at the specified index of the collection.
+ /// </summary>
+ /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
+ /// <param name="item">The item to add.</param>
+ [CLSCompliant(false)]
+ public void Insert(int index, sbyte item) => Insert(index, new JsonNumber(item));
+
+ /// <summary>
+ /// Inserts the specified item as a <see cref="JsonNumber"/> at the specified index of the collection.
+ /// </summary>
+ /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
+ /// <param name="item">The item to add.</param>
+ [CLSCompliant(false)]
+ public void Insert(int index, ushort item) => Insert(index, new JsonNumber(item));
+
+ /// <summary>
+ /// Inserts the specified item as a <see cref="JsonNumber"/> at the specified index of the collection.
+ /// </summary>
+ /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
+ /// <param name="item">The item to add.</param>
+ [CLSCompliant(false)]
+ public void Insert(int index, uint item) => Insert(index, new JsonNumber(item));
+
+ /// <summary>
+ /// Inserts the specified item as a <see cref="JsonNumber"/> at the specified index of the collection.
+ /// </summary>
+ /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
+ /// <param name="item">The item to add.</param>
+ [CLSCompliant(false)]
+ public void Insert(int index, ulong item) => Insert(index, new JsonNumber(item));
+
+ /// <summary>
+ /// Inserts the specified item as a <see cref="JsonNumber"/> at the specified index of the collection.
+ /// </summary>
+ /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
+ /// <param name="item">The item to add.</param>
+ public void Insert(int index, decimal item) => Insert(index, new JsonNumber(item));
+
+ /// <summary>
+ /// Determines whether a specified <see cref="JsonNode"/> element is in a collection.
+ /// </summary>
+ /// <param name="value">Value to check.</param>
+ /// <returns>
+ /// <see langword="true"/> if the value is successfully found in a collection,
+ /// <see langword="false"/> otherwise.
+ /// </returns>
+ public bool Contains(JsonNode value) => _list.Contains(value);
+
+ /// <summary>
+ /// Gets the number of elements contained in the collection.
+ /// </summary>
+ public int Count => _list.Count;
+
+ /// <summary>
+ /// Gets a value indicating whether the collection is read-only.
+ /// </summary>
+ public bool IsReadOnly => false;
+
+ /// <summary>
+ /// Returns the zero-based index of the first occurrence of a specified item in the collection.
+ /// </summary>
+ /// <param name="item">Item to find.</param>
+ /// <returns>The zero-based starting index of the search. 0 (zero) is valid in an empty collection.</returns>
+ public int IndexOf(JsonNode item) => _list.IndexOf(item);
+
+ /// <summary>
+ /// Returns the zero-based index of the last occurrence of a specified item in the collection.
+ /// </summary>
+ /// <param name="item">Item to find.</param>
+ /// <returns>The zero-based starting index of the search. 0 (zero) is valid in an empty collection.</returns>
+ public int LastIndexOf(JsonNode item) => _list.LastIndexOf(item);
+
+ /// <summary>
+ /// Removes all elements from the JSON array.
+ /// </summary>
+ public void Clear()
+ {
+ _list.Clear();
+ _version++;
+ }
+
+ /// <summary>
+ /// Removes the first occurrence of a specific object from the collection.
+ /// </summary>
+ /// <param name="item">
+ /// The object to remove from the collection. The value can be null and it represents null collection.
+ /// </param>
+ /// <returns>
+ /// <see langword="true"/> if the item is successfully found in a collection and removed,
+ /// <see langword="false"/> otherwise.
+ /// </returns>
+ public bool Remove(JsonNode item)
+ {
+ _version++;
+ return _list.Remove(item);
+ }
+
+ /// <summary>
+ /// Removes all the elements that match the conditions defined by the specified predicate.
+ /// </summary>
+ /// <param name="match">
+ /// Thepredicate delegate that defines the conditions of the elements to remove.
+ /// </param>
+ /// <returns>The number of elements removed from the collection.</returns>
+ public int RemoveAll(Predicate<JsonNode> match)
+ {
+ _version++;
+ return _list.RemoveAll(match);
+ }
+ /// <summary>
+ /// Removes the item at the specified index of the collection.
+ /// </summary>
+ /// <param name="index">
+ /// The zero-based index of the element to remove.
+ /// </param>
+ public void RemoveAt(int index)
+ {
+ _list.RemoveAt(index);
+ _version++;
+ }
+
+ /// <summary>
+ /// Copies the collection or a portion of it to an array.
+ /// </summary>
+ /// <param name="array">
+ /// The one-dimensional array that is the destination of the elements copied from collection.
+ /// The array must have zero-based indexing.
+ /// </param>
+ /// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
+ void ICollection<JsonNode>.CopyTo(JsonNode[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);
+
+ /// <summary>
+ /// Returns an enumerator that iterates through the collection values.
+ /// </summary>
+ /// <returns>An enumerator structure for the <see cref="JsonArray"/>.</returns>
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ /// <summary>
+ /// Returns an enumerator that iterates through the collection values.
+ /// </summary>
+ /// <returns>An enumerator structure for the <see cref="JsonArray"/>.</returns>
+ IEnumerator<JsonNode> IEnumerable<JsonNode>.GetEnumerator() => new JsonArrayEnumerator(this);
+
+ /// <summary>
+ /// Returns an enumerator that iterates through the collection values.
+ /// </summary>
+ /// <returns>An enumerator structure for the <see cref="JsonArray"/>.</returns>
+ public JsonArrayEnumerator GetEnumerator() => new JsonArrayEnumerator(this);
+
+ /// <summary>
+ /// Creates a new collection that is a copy of the current instance.
+ /// </summary>
+ /// <returns>A new collection that is a copy of this instance.</returns>
+ public override JsonNode Clone()
+ {
+ var jsonArray = new JsonArray();
+
+ foreach (JsonNode jsonNode in _list)
+ {
+ jsonArray.Add(jsonNode.Clone());
+ }
+
+ return jsonArray;
+ }
+
+ /// <summary>
+ /// Returns <see cref="JsonValueKind.Array"/>
+ /// </summary>
+ public override JsonValueKind ValueKind { get => JsonValueKind.Array;}
+ }
+}
--- /dev/null
+using System.Collections;
+using System.Collections.Generic;
+
+namespace System.Text.Json
+{
+ /// <summary>
+ /// Supports an iteration over a JSON array.
+ /// </summary>
+ public struct JsonArrayEnumerator : IEnumerator<JsonNode>
+ {
+ private List<JsonNode>.Enumerator _enumerator;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonArrayEnumerator"/> class supporting an interation over provided JSON array.
+ /// </summary>
+ /// <param name="jsonArray">JSON array to iterate over.</param>
+ public JsonArrayEnumerator(JsonArray jsonArray) => _enumerator = jsonArray._list.GetEnumerator();
+
+ /// <summary>
+ /// Gets the property in the JSON array at the current position of the enumerator.
+ /// </summary>
+ public JsonNode Current => _enumerator.Current;
+
+ /// <summary>
+ /// Gets the property in the JSON array at the current position of the enumerator.
+ /// </summary>
+ object IEnumerator.Current => _enumerator.Current;
+
+ /// <summary>
+ /// Releases all resources used by the <see cref="JsonObjectEnumerator"/>.
+ /// </summary>
+ public void Dispose() => _enumerator.Dispose();
+
+ /// <summary>
+ /// Advances the enumerator to the next property of the JSON array.
+ /// </summary>
+ /// <returns></returns>
+ public bool MoveNext() => _enumerator.MoveNext();
+
+ /// <summary>
+ /// Sets the enumerator to its initial position, which is before the first element in the JSON array.
+ /// </summary>
+ void IEnumerator.Reset() => ((IEnumerator)_enumerator).Reset();
+ }
+}
/// <see langword="false"/> otherwise.
/// </returns>
public static bool operator !=(JsonBoolean left, JsonBoolean right) => !(left == right);
+
+ /// <summary>
+ /// Creates a new JSON boolean that is a copy of the current instance.
+ /// </summary>
+ /// <returns>A new JSON boolean that is a copy of this instance.</returns>
+ public override JsonNode Clone() => new JsonBoolean(Value);
+
+ /// <summary>
+ /// Returns <see cref="JsonValueKind.True"/> or <see cref="JsonValueKind.False"/>, accordingly to the represented value.
+ /// </summary>
+ public override JsonValueKind ValueKind { get => Value ? JsonValueKind.True : JsonValueKind.False; }
}
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Diagnostics;
+
namespace System.Text.Json
{
/// <summary>
public abstract class JsonNode
{
private protected JsonNode() { }
+
+ /// <summary>
+ /// Transforms this instance into <see cref="JsonElement"/> representation.
+ /// Operations performed on this instance will modify the returned <see cref="JsonElement"/>.
+ /// </summary>
+ /// <returns>Mutable <see cref="JsonElement"/> with <see cref="JsonNode"/> underneath.</returns>
+ public JsonElement AsJsonElement() => new JsonElement(this);
+
+ /// <summary>
+ /// The <see cref="JsonValueKind"/> that the node is.
+ /// </summary>
+ public abstract JsonValueKind ValueKind { get; }
+
+ /// <summary>
+ /// Gets the <see cref="JsonNode"/> represented by <paramref name="jsonElement"/>.
+ /// Operations performed on the returned <see cref="JsonNode"/> will modify the <paramref name="jsonElement"/>.
+ /// </summary>
+ /// <param name="jsonElement"><see cref="JsonElement"/> to get the <see cref="JsonNode"/> from.</param>
+ /// <returns><see cref="JsonNode"/> represented by <paramref name="jsonElement"/>.</returns>
+ public static JsonNode GetNode(JsonElement jsonElement) => (JsonNode)jsonElement._parent;
+
+ /// <summary>
+ /// Gets the <see cref="JsonNode"/> represented by the <paramref name="jsonElement"/>.
+ /// Operations performed on the returned <see cref="JsonNode"/> will modify the <paramref name="jsonElement"/>.
+ /// A return value indicates whether the operation succeeded.
+ /// </summary>
+ /// <param name="jsonElement"><see cref="JsonElement"/> to get the <see cref="JsonNode"/> from.</param>
+ /// <param name="jsonNode"><see cref="JsonNode"/> represented by <paramref name="jsonElement"/>.</param>
+ /// <returns>
+ /// <see langword="true"/> if the operation succeded;
+ /// otherwise, <see langword="false"/>
+ /// </returns>
+ public static bool TryGetNode(JsonElement jsonElement, out JsonNode jsonNode)
+ {
+ if (!jsonElement.IsImmutable)
+ {
+ jsonNode = (JsonNode)jsonElement._parent;
+ return true;
+ }
+
+ jsonNode = null;
+ return false;
+ }
+
+ /// <summary>
+ /// Parses a string representiong JSON document into <see cref="JsonNode"/>.
+ /// </summary>
+ /// <param name="json">JSON to parse.</param>
+ /// <returns><see cref="JsonNode"/> representation of <paramref name="json"/>.</returns>
+ public static JsonNode Parse(string json)
+ {
+ using (JsonDocument document = JsonDocument.Parse(json))
+ {
+ return DeepCopy(document.RootElement);
+ }
+ }
+
+ /// <summary>
+ /// Performs a deep copy operation on this instance.
+ /// </summary>
+ /// <returns><see cref="JsonNode"/> which is a clone of this instance.</returns>
+ public abstract JsonNode Clone();
+
+ /// <summary>
+ /// Performs a deep copy operation on <paramref name="jsonElement"/> returning corresponding modifiable tree structure of JSON nodes.
+ /// Operations performed on returned <see cref="JsonNode"/> does not modify <paramref name="jsonElement"/>.
+ /// </summary>
+ /// <param name="jsonElement"><see cref="JsonElement"/> to copy.</param>
+ /// <returns><see cref="JsonNode"/> representing <paramref name="jsonElement"/>.</returns>
+ public static JsonNode DeepCopy(JsonElement jsonElement)
+ {
+ if (!jsonElement.IsImmutable)
+ {
+ return GetNode(jsonElement).Clone();
+ }
+
+ switch (jsonElement.ValueKind)
+ {
+ case JsonValueKind.Object:
+ JsonObject jsonObject = new JsonObject();
+ foreach (JsonProperty property in jsonElement.EnumerateObject())
+ {
+ jsonObject.Add(property.Name, DeepCopy(property.Value));
+ }
+ return jsonObject;
+ case JsonValueKind.Array:
+ JsonArray jsonArray = new JsonArray();
+ foreach (JsonElement element in jsonElement.EnumerateArray())
+ {
+ jsonArray.Add(DeepCopy(element));
+ }
+ return jsonArray;
+ case JsonValueKind.Number:
+ return new JsonNumber(jsonElement.GetRawText());
+ case JsonValueKind.String:
+ return new JsonString(jsonElement.GetString());
+ case JsonValueKind.True:
+ return new JsonBoolean(true);
+ case JsonValueKind.False:
+ return new JsonBoolean(false);
+ case JsonValueKind.Null:
+ return null;
+ default:
+ Debug.Assert(jsonElement.ValueKind == JsonValueKind.Undefined, "No handler for JsonValueKind.{jsonElement.ValueKind}");
+ throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Undefined, jsonElement.ValueKind);
+ }
+ }
}
}
/// <see langword="false"/> otherwise.
/// </returns>
public static bool operator !=(JsonNumber left, JsonNumber right) => !(left == right);
+
+ /// <summary>
+ /// Creates a new JSON number that is a copy of the current instance.
+ /// </summary>
+ /// <returns>A new JSON number that is a copy of this instance.</returns>
+ public override JsonNode Clone() => new JsonNumber(_value);
+
+ /// <summary>
+ /// Returns <see cref="JsonValueKind.Number"/>
+ /// </summary>
+ public override JsonValueKind ValueKind { get => JsonValueKind.Number; }
}
}
}
/// <summary>
- /// Returns an enumerator that iterates through the JSON object properties.
- /// </summary>
- /// <returns>An enumerator structure for the JSON object.</returns>
- /// <exception cref="ArgumentException">
- /// Property name to set already exists if handling duplicates is set to <see cref="DuplicatePropertyNameHandling.Error"/>.
- /// </exception>
- public IEnumerator<KeyValuePair<string, JsonNode>> GetEnumerator() => new JsonObjectEnumerator(this);
-
- /// <summary>
/// Adds the specified property to the JSON object.
/// </summary>
/// <param name="jsonProperty">The property to add.</param>
/// 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>
+ /// <param name="propertyValue"><see cref="ReadOnlySpan{Char}"/> value of the property to add.</param>
/// <exception cref="ArgumentException">
/// Property name to set already exists if handling duplicates is set to <see cref="DuplicatePropertyNameHandling.Error"/>.
/// </exception>
}
/// <summary>
+ /// Adds the property values from the specified collection as a <see cref="JsonArray"/> property to the JSON object.
+ /// </summary>
+ /// <param name="propertyName">Name of the <see cref="JsonArray"/> property to add.</param>
+ /// <param name="propertyValues">Properties to add.</param>
+ /// <exception cref="ArgumentException">
+ /// Provided collection contains duplicates if handling duplicates is set to <see cref="DuplicatePropertyNameHandling.Error"/>.
+ /// </exception>
+ /// <exception cref="ArgumentNullException">
+ /// Some of property names are null.
+ /// </exception>
+ public void Add(string propertyName, IEnumerable<JsonNode> propertyValues)
+ {
+ var jsonArray = new JsonArray();
+ foreach (JsonNode value in propertyValues)
+ {
+ jsonArray.Add(value);
+ }
+ Add(propertyName, (JsonNode)jsonArray);
+ }
+
+ /// <summary>
/// Removes the property with the specified name.
/// </summary>
/// <param name="propertyName"></param>
/// <exception cref="KeyNotFoundException">
/// Property with specified name is not found in JSON object.
/// </exception>
- /// <exception cref="InvalidCastException">
+ /// <exception cref="ArgumentException">
/// Property with specified name is not a JSON object.
/// </exception>
public JsonObject GetJsonObjectPropertyValue(string propertyName)
return jsonObject;
}
- throw new InvalidCastException(SR.Format(SR.PropertyTypeMismatch, propertyName));
+ throw new ArgumentException(SR.Format(SR.PropertyTypeMismatch, propertyName));
}
/// <summary>
{
if (TryGetPropertyValue(propertyName, out JsonNode jsonNode))
{
- if (jsonNode is JsonObject jsonNodeCasted)
- {
- jsonObject = jsonNodeCasted;
- return true;
- }
+ jsonObject = jsonNode as JsonObject;
+ return jsonObject != null;
}
jsonObject = null;
}
/// <summary>
+ /// Returns the JSON array value of a property with the specified name.
+ /// </summary>
+ /// <param name="propertyName">Name of the property to return.</param>
+ /// <returns>JSON objectvalue of a property with the specified name.</returns>
+ /// <exception cref="KeyNotFoundException">
+ /// Property with specified name is not found in JSON array.
+ /// </exception>
+ /// <exception cref="ArgumentException">
+ /// Property with specified name is not a JSON array.
+ /// </exception>
+ public JsonArray GetJsonArrayPropertyValue(string propertyName)
+ {
+ if (GetPropertyValue(propertyName) is JsonArray jsonArray)
+ {
+ return jsonArray;
+ }
+
+ throw new ArgumentException(SR.Format(SR.PropertyTypeMismatch, propertyName));
+ }
+
+ /// <summary>
+ /// Returns the JSON array value of a property with the specified name.
+ /// </summary>
+ /// <param name="propertyName">Name of the property to return.</param>
+ /// <param name="jsonArray">JSON array value of the property with specified name.</param>
+ /// <returns>
+ /// <see langword="true"/> if JSON array property with specified name was found;
+ /// otherwise, <see langword="false"/>
+ /// </returns>
+ public bool TryGetJsonArrayPropertyValue(string propertyName, out JsonArray jsonArray)
+ {
+ if (TryGetPropertyValue(propertyName, out JsonNode jsonNode))
+ {
+ jsonArray = jsonNode as JsonArray;
+ return jsonArray != null;
+ }
+
+ jsonArray = null;
+ return false;
+ }
+
+ /// <summary>
/// A collection containing the property names of JSON object.
/// </summary>
public ICollection<string> PropertyNames => _dictionary.Keys;
/// Returns an enumerator that iterates through the JSON object properties.
/// </summary>
/// <returns>An enumerator structure for the <see cref="JsonObject"/>.</returns>
- IEnumerator IEnumerable.GetEnumerator() => new JsonObjectEnumerator(this);
+ /// <exception cref="ArgumentException">
+ /// Property name to set already exists if handling duplicates is set to <see cref="DuplicatePropertyNameHandling.Error"/>.
+ /// </exception>
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ /// <summary>
+ /// Returns an enumerator that iterates through the JSON object properties.
+ /// </summary>
+ /// <returns>An enumerator structure for the JSON object.</returns>
+ /// <exception cref="ArgumentException">
+ /// Property name to set already exists if handling duplicates is set to <see cref="DuplicatePropertyNameHandling.Error"/>.
+ /// </exception>
+ IEnumerator<KeyValuePair<string, JsonNode>> IEnumerable<KeyValuePair<string, JsonNode>>.GetEnumerator() => new JsonObjectEnumerator(this);
+
+ /// <summary>
+ /// Returns an enumerator that iterates through the JSON object properties.
+ /// </summary>
+ /// <returns>An enumerator structure for the JSON object.</returns>
+ /// <exception cref="ArgumentException">
+ /// Property name to set already exists if handling duplicates is set to <see cref="DuplicatePropertyNameHandling.Error"/>.
+ /// </exception>
+ public JsonObjectEnumerator GetEnumerator() => new JsonObjectEnumerator(this);
+
+ /// <summary>
+ /// Creates a new JSON object that is a copy of the current instance.
+ /// </summary>
+ /// <returns>A new JSON object that is a copy of this instance.</returns>
+ public override JsonNode Clone()
+ {
+ var jsonObject = new JsonObject(_duplicatePropertyNameHandling);
+
+ foreach (KeyValuePair<string, JsonNode> property in _dictionary)
+ {
+ jsonObject.Add(property.Key, property.Value.Clone());
+ }
+
+ return jsonObject;
+ }
+
+ /// <summary>
+ /// Returns <see cref="JsonValueKind.Object"/>
+ /// </summary>
+ public override JsonValueKind ValueKind { get => JsonValueKind.Object; }
}
}
/// </summary>
public struct JsonObjectEnumerator : IEnumerator<KeyValuePair<string, JsonNode>>
{
- private readonly IEnumerator<KeyValuePair<string, JsonNode>> _enumerator;
+ private Dictionary<string, JsonNode>.Enumerator _enumerator;
/// <summary>
/// Initializes a new instance of the <see cref="JsonObjectEnumerator"/> class supporting an interation over provided JSON object.
/// <summary>
/// Sets the enumerator to its initial position, which is before the first element in the JSON object.
/// </summary>
- public void Reset() => _enumerator.Reset();
+ void IEnumerator.Reset() => ((IEnumerator)_enumerator).Reset();
}
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Globalization;
+
namespace System.Text.Json
{
/// <summary>
/// Initializes a new instance of the <see cref="JsonString"/> with a string representation of the <see cref="Guid"/> structure.
/// </summary>
/// <param name="value">The value to represent as a JSON string.</param>
- public JsonString(Guid value) => Value = value.ToString();
+ public JsonString(Guid value) => Value = value.ToString("D");
/// <summary>
- /// Initializes a new instance of the <see cref="JsonString"/> with a string representation of the <see cref="DateTime"/> structure.
+ /// Initializes a new instance of the <see cref="JsonString"/> with an ISO 8601 representation of the <see cref="DateTime"/> structure.
/// </summary>
/// <param name="value">The value to represent as a JSON string.</param>
- public JsonString(DateTime value) => Value = value.ToString();
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// The date and time is outside the range of dates supported by the calendar used by the invariant culture.
+ /// </exception>
+ public JsonString(DateTime value) => Value = value.ToString("s", CultureInfo.InvariantCulture);
/// <summary>
- /// Initializes a new instance of the <see cref="JsonString"/> with a string representation of the <see cref="DateTimeOffset"/> structure.
+ /// Initializes a new instance of the <see cref="JsonString"/> with an ISO 8601 representation of the <see cref="DateTimeOffset"/> structure.
/// </summary>
/// <param name="value">The value to represent as a JSON string.</param>
- public JsonString(DateTimeOffset value) => Value = value.ToString();
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// The date and time is outside the range of dates supported by the calendar used by the invariant culture.
+ /// </exception>
+ public JsonString(DateTimeOffset value) => Value = value.ToString("s", CultureInfo.InvariantCulture);
/// <summary>
/// Gets or sets the text value represented by the instance.
public override string ToString() => _value;
/// <summary>
+ /// Converts the ISO 8601 text value of this instance to its <see cref="DateTime"/> equivalent.
+ /// </summary>
+ /// <returns>A <see cref="DateTime"/> equivalent to the text stored by this instance.</returns>
+ /// <exception cref="FormatException">
+ /// Text value of this instance is not in an ISO 8601 defined DateTime format.
+ /// </exception>
+ public DateTime GetDateTime() => DateTime.ParseExact(_value, "s", CultureInfo.InvariantCulture);
+
+ /// <summary>
+ /// Converts the ISO 8601 text value of this instance to <see cref="DateTimeOffset"/> equivalent.
+ /// </summary>
+ /// <returns>A <see cref="DateTimeOffset"/> equivalent to the text stored by this instance.</returns>
+ /// <exception cref="FormatException">
+ /// Text value of this instance is not in an ISO 8601 defined DateTimeOffset format.
+ /// </exception>
+ public DateTimeOffset GetDateTimeOffset() => DateTimeOffset.ParseExact(_value, "s", CultureInfo.InvariantCulture);
+
+ /// <summary>
+ /// Converts the text value of this instance to its <see cref="Guid"/> equivalent.
+ /// </summary>
+ /// <returns>A <see cref="Guid"/> equivalent to the text stored by this instance.</returns>
+ /// <exception cref="FormatException">
+ /// Text value of this instance is not in a GUID recognized format.
+ /// </exception>
+ public Guid GetGuid() => Guid.ParseExact(_value, "D");
+
+ /// <summary>
+ /// Converts the ISO 8601 text value of this instance to its ISO 8601 <see cref="DateTime"/> equivalent.
+ /// A return value indicates whether the conversion succeeded.
+ /// </summary>
+ /// <param name="value">
+ /// When this method returns, contains the see cref="DateTime"/> value equivalent of the text contained in this instance,
+ /// if the conversion succeeded, or zero if the conversion failed.
+ /// </param>
+ /// <returns>
+ /// <see langword="true"/> if instance was converted successfully;
+ /// otherwise, <see langword="false"/>
+ /// </returns>
+ public bool TryGetDateTime(out DateTime value) => DateTime.TryParseExact(_value, "s", CultureInfo.InvariantCulture, DateTimeStyles.None, out value);
+
+ /// <summary>
+ /// Converts the ISO 8601 text value of this instance to its <see cref="DateTimeOffset"/> equivalent.
+ /// A return value indicates whether the conversion succeeded.
+ /// </summary>
+ /// <param name="value">
+ /// When this method returns, contains the <see cref="DateTimeOffset"/> value equivalent of the text contained in this instance,
+ /// if the conversion succeeded, or zero if the conversion failed.
+ /// </param>
+ /// <returns>
+ /// <see langword="true"/> if instance was converted successfully;
+ /// otherwise, <see langword="false"/>
+ /// </returns>
+ public bool TryGetDateTimeOffset(out DateTimeOffset value) => DateTimeOffset.TryParseExact(_value, "s", CultureInfo.InvariantCulture, DateTimeStyles.None, out value);
+
+ /// <summary>
+ /// Converts the text value of this instance to its <see cref="Guid"/> equivalent.
+ /// </summary>
+ /// <returns>A <see cref="Guid"/> equivalent to the text stored by this instance.</returns>
+ /// <exception cref="FormatException">
+ /// Text value of this instance is not in a GUID recognized format.
+ /// </exception>
+ public bool TryGetGuid(out Guid value) => Guid.TryParseExact(_value, "D", out value);
+
+ /// <summary>
/// Converts a <see cref="string"/> to a <see cref="JsonString"/>.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <see langword="false"/> otherwise.
/// </returns>
public static bool operator !=(JsonString left, JsonString right) => !(left == right);
+
+ /// <summary>
+ /// Creates a new JSON string that is a copy of the current instance.
+ /// </summary>
+ /// <returns>A new JSON string that is a copy of this instance.</returns>
+ public override JsonNode Clone() => new JsonString(Value);
+
+ /// <summary>
+ /// Returns <see cref="JsonValueKind.String"/>
+ /// </summary>
+ public override JsonValueKind ValueKind { get => JsonValueKind.String; }
}
}
JsonTokenType expectedType,
JsonTokenType actualType)
{
- return GetInvalidOperationException(
- SR.Format(SR.JsonElementHasWrongType, expectedType.ToValueKind(), actualType.ToValueKind()));
+ return GetJsonElementWrongTypeException(expectedType.ToValueKind(), actualType.ToValueKind());
}
[MethodImpl(MethodImplOptions.NoInlining)]
string expectedTypeName,
JsonTokenType actualType)
{
+ return GetJsonElementWrongTypeException(expectedTypeName, actualType.ToValueKind());
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ internal static InvalidOperationException GetJsonElementWrongTypeException(
+ JsonValueKind expectedType,
+ JsonValueKind actualType)
+ {
+ return GetInvalidOperationException(
+ SR.Format(SR.JsonElementHasWrongType, expectedType, actualType));
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ internal static InvalidOperationException GetJsonElementWrongTypeException(
+ string expectedTypeName,
+ JsonValueKind actualType)
+ {
return GetInvalidOperationException(
- SR.Format(SR.JsonElementHasWrongType, expectedTypeName, actualType.ToValueKind()));
+ SR.Format(SR.JsonElementHasWrongType, expectedTypeName, actualType));
}
public static void ThrowJsonReaderException(ref Utf8JsonReader json, ExceptionResource resource, byte nextByte = default, ReadOnlySpan<byte> bytes = default)
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+
+namespace System.Text.Json.Tests
+{
+ public static class JsonArrayTests
+ {
+
+ private static void TestArray<T>(T value1, T value2, Func<JsonArray, T> getter, Func<T, JsonNode> nodeCtor)
+ {
+ var value1Casted = value1 as dynamic;
+ var value2Casted = value2 as dynamic;
+
+ JsonNode value1Node = nodeCtor(value1);
+ JsonNode value2Node = nodeCtor(value2);
+
+ var list = new List<T>() { value1 };
+ JsonArray jsonArray = new JsonArray(list as dynamic);
+ Assert.Equal(1, jsonArray.Count);
+
+ Assert.True(jsonArray.Contains(value1Node));
+ Assert.Equal(value1, getter(jsonArray));
+
+ jsonArray.Insert(0, value2 as dynamic);
+ Assert.Equal(2, jsonArray.Count);
+ Assert.True(jsonArray.Contains(value2Node));
+ Assert.Equal(value2, getter(jsonArray));
+
+ Assert.Equal(1, jsonArray.IndexOf(value1Node));
+ Assert.Equal(1, jsonArray.LastIndexOf(value1Node));
+
+ jsonArray.RemoveAt(0);
+ Assert.False(jsonArray.Contains(value2Node));
+
+ jsonArray.Remove(value1Node);
+ Assert.False(jsonArray.Contains(value1Node));
+
+ Assert.Equal(0, jsonArray.Count);
+
+ jsonArray.Add(value2Casted);
+ Assert.Equal(1, jsonArray.Count);
+ Assert.True(jsonArray.Contains(value2Node));
+ Assert.Equal(value2, getter(jsonArray));
+
+ jsonArray[0] = value1Node;
+ Assert.Equal(1, jsonArray.Count);
+ Assert.True(jsonArray.Contains(value1Node));
+ Assert.Equal(value1, getter(jsonArray));
+ }
+
+ [Fact]
+ public static void TestStringArray()
+ {
+ TestArray(
+ "value1", "value2",
+ jsonArray => ((JsonString)jsonArray[0]).Value,
+ v => new JsonString(v)
+ );
+ }
+
+ [Fact]
+ public static void TestBooleanArray()
+ {
+ TestArray(
+ true, false,
+ jsonArray => ((JsonBoolean)jsonArray[0]).Value,
+ v => new JsonBoolean(v)
+ );
+ }
+
+ [Fact]
+ public static void TestByteArray()
+ {
+ TestArray<byte>(
+ byte.MaxValue, byte.MaxValue - 1,
+ jsonArray => ((JsonNumber)jsonArray[0]).GetByte(),
+ v => new JsonNumber(v)
+ );
+ }
+
+ [Fact]
+ public static void TestInt16Array()
+ {
+ TestArray<short>(
+ short.MaxValue, short.MaxValue - 1,
+ jsonArray => ((JsonNumber)jsonArray[0]).GetInt16(),
+ v => new JsonNumber(v)
+ );
+ }
+
+ [Fact]
+ public static void TestInt32rray()
+ {
+ TestArray(
+ int.MaxValue, int.MaxValue - 1,
+ jsonArray => ((JsonNumber)jsonArray[0]).GetInt32(),
+ v => new JsonNumber(v)
+ );
+ }
+
+ [Fact]
+ public static void TestInt64rray()
+ {
+ TestArray(
+ long.MaxValue, long.MaxValue - 1,
+ jsonArray => ((JsonNumber)jsonArray[0]).GetInt64(),
+ v => new JsonNumber(v)
+ );
+ }
+
+ [Fact]
+ public static void TestSingleArray()
+ {
+ TestArray(
+ 3.14f, 1.41f,
+ jsonArray => ((JsonNumber)jsonArray[0]).GetSingle(),
+ v => new JsonNumber(v)
+ );
+ }
+
+ [Fact]
+ public static void TestDoubleArray()
+ {
+ TestArray(
+ 3.14, 1.41,
+ jsonArray => ((JsonNumber)jsonArray[0]).GetDouble(),
+ v => new JsonNumber(v)
+ );
+ }
+
+ [Fact]
+ public static void TestSByteArray()
+ {
+ TestArray<sbyte>(
+ sbyte.MaxValue, sbyte.MaxValue - 1,
+ jsonArray => ((JsonNumber)jsonArray[0]).GetSByte(),
+ v => new JsonNumber(v)
+ );
+ }
+
+ [Fact]
+ public static void TestUInt16Array()
+ {
+ TestArray<ushort>(
+ ushort.MaxValue, ushort.MaxValue - 1,
+ jsonArray => ((JsonNumber)jsonArray[0]).GetUInt16(),
+ v => new JsonNumber(v)
+ );
+ }
+
+ [Fact]
+ public static void TestUInt32Array()
+ {
+ TestArray(
+ uint.MaxValue, uint.MaxValue - 1,
+ jsonArray => ((JsonNumber)jsonArray[0]).GetUInt32(),
+ v => new JsonNumber(v)
+ );
+ }
+
+ [Fact]
+ public static void TestUInt64Array()
+ {
+ TestArray(
+ ulong.MaxValue, ulong.MaxValue - 1,
+ jsonArray => ((JsonNumber)jsonArray[0]).GetUInt64(),
+ v => new JsonNumber(v)
+ );
+ }
+
+ [Fact]
+ public static void TestDecimalArray()
+ {
+ TestArray(
+ decimal.One, decimal.Zero,
+ jsonArray => ((JsonNumber)jsonArray[0]).GetDecimal(),
+ v => new JsonNumber(v)
+ );
+ }
+
+ [Fact]
+ public static void TestCreatingJsonArrayFromStringArray()
+ {
+ string[] expected = { "sushi", "pasta", "cucumber soup" };
+
+ var dishesJsonArray = new JsonArray(expected);
+ Assert.Equal(3, dishesJsonArray.Count);
+
+ for (int i = 0; i < dishesJsonArray.Count; i++)
+ {
+ Assert.IsType<JsonString>(dishesJsonArray[i]);
+ Assert.Equal(expected[i], dishesJsonArray[i] as JsonString);
+ }
+ }
+
+ [Fact]
+ public static void TestCreatingNestedJsonArray()
+ {
+ var vertices = new JsonArray()
+ {
+ new JsonArray
+ {
+ new JsonArray
+ {
+ new JsonArray { 0, 0, 0 },
+ new JsonArray { 0, 0, 1 }
+ },
+ new JsonArray
+ {
+ new JsonArray { 0, 1, 0 },
+ new JsonArray { 0, 1, 1 }
+ }
+ },
+ new JsonArray
+ {
+ new JsonArray
+ {
+ new JsonArray { 1, 0, 0 },
+ new JsonArray { 1, 0, 1 }
+ },
+ new JsonArray
+ {
+ new JsonArray { 1, 1, 0 },
+ new JsonArray { 1, 1, 1 }
+ }
+ },
+ };
+
+ var innerJsonArray = (JsonArray)vertices[0];
+ innerJsonArray = (JsonArray)innerJsonArray[0];
+ Assert.IsType<JsonArray>(innerJsonArray[0]);
+ }
+
+ [Fact]
+ public static void TestCreatingJsonArrayFromCollection()
+ {
+ var employeesIds = new JsonArray(EmployeesDatabase.GetTenBestEmployees().Select(employee => new JsonString(employee.Key)));
+
+ JsonString prevId = new JsonString();
+ foreach (JsonNode employeeId in employeesIds)
+ {
+ var employeeIdString = (JsonString)employeeId;
+ Assert.NotEqual(prevId, employeeIdString);
+ prevId = employeeIdString;
+ }
+ }
+
+ [Fact]
+ public static void TestCreatingJsonArrayFromCollectionOfString()
+ {
+ var employeesIds = new JsonArray(EmployeesDatabase.GetTenBestEmployees().Select(employee => employee.Key));
+
+ JsonString prevId = new JsonString();
+ foreach (JsonNode employeeId in employeesIds)
+ {
+ var employeeIdString = (JsonString)employeeId;
+ Assert.NotEqual(prevId, employeeIdString);
+ prevId = employeeIdString;
+ }
+ }
+
+ [Fact]
+ public static void TestAddingToJsonArray()
+ {
+ var employeesIds = new JsonArray();
+
+ foreach (KeyValuePair<string, JsonNode> employee in EmployeesDatabase.GetTenBestEmployees())
+ {
+ employeesIds.Add(employee.Key);
+ }
+
+ JsonString prevId = new JsonString();
+ foreach (JsonNode employeeId in employeesIds)
+ {
+ var employeeIdString = (JsonString)employeeId;
+ Assert.NotEqual(prevId, employeeIdString);
+ prevId = employeeIdString;
+ }
+ }
+
+ [Fact]
+ public static void TestAccesingNestedJsonArrayGetPropertyMethod()
+ {
+ var issues = new JsonObject()
+ {
+ { "features", new JsonString [] { "new functionality 1", "new functionality 2" } },
+ { "bugs", new JsonString [] { "bug 123", "bug 4566", "bug 821" } },
+ { "tests", new JsonString [] { "code coverage" } },
+ };
+
+ issues.GetJsonArrayPropertyValue("bugs").Add("bug 12356");
+ ((JsonString)issues.GetJsonArrayPropertyValue("features")[0]).Value = "feature 1569";
+ ((JsonString)issues.GetJsonArrayPropertyValue("features")[1]).Value = "feature 56134";
+
+ Assert.Equal((JsonString)"bug 12356", ((JsonArray)issues["bugs"])[3]);
+ Assert.Equal("feature 1569", (JsonString)((JsonArray)issues["features"])[0]);
+ Assert.Equal("feature 56134", (JsonString)((JsonArray)issues["features"])[1]);
+ }
+
+ [Fact]
+ public static void TestAccesingNestedJsonArrayTryGetPropertyMethod()
+ {
+ var issues = new JsonObject()
+ {
+ { "features", new JsonString [] { "new functionality 1", "new functionality 2" } },
+ };
+
+ Assert.True(issues.TryGetJsonArrayPropertyValue("features", out JsonArray featuresArray));
+ Assert.Equal("new functionality 1", (JsonString)featuresArray[0]);
+ Assert.Equal("new functionality 2", (JsonString)featuresArray[1]);
+ }
+
+ [Fact]
+ public static void TestOutOfRangeException()
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(() => new JsonArray()[-1]);
+ Assert.Throws<ArgumentOutOfRangeException>(() => new JsonArray()[-1] = new JsonString());
+ Assert.Throws<ArgumentOutOfRangeException>(() => new JsonArray()[0]);
+ Assert.Throws<ArgumentOutOfRangeException>(() => new JsonArray()[0] = new JsonString());
+ Assert.Throws<ArgumentOutOfRangeException>(() => new JsonArray()[1]);
+ Assert.Throws<ArgumentOutOfRangeException>(() => new JsonArray()[1] = new JsonString());
+ }
+
+ [Fact]
+ public static void TestIsReadOnly()
+ {
+ Assert.False(new JsonArray().IsReadOnly);
+ }
+
+ [Fact]
+ public static void TestClean()
+ {
+ var jsonArray = new JsonArray { 1, 2, 3 };
+
+ Assert.Equal(3, jsonArray.Count);
+ Assert.Equal((JsonNumber)1, jsonArray[0]);
+ Assert.Equal((JsonNumber)2, jsonArray[1]);
+ Assert.Equal((JsonNumber)3, jsonArray[2]);
+
+ jsonArray.Clear();
+
+ Assert.Equal(0, jsonArray.Count);
+ }
+
+ [Fact]
+ public static void TestRemoveAll()
+ {
+ var jsonArray = new JsonArray { 1, 2, 3 };
+
+ Assert.Equal(3, jsonArray.Count);
+ Assert.Equal((JsonNumber)1, jsonArray[0]);
+ Assert.Equal((JsonNumber)2, jsonArray[1]);
+ Assert.Equal((JsonNumber)3, jsonArray[2]);
+
+ jsonArray.RemoveAll(v => ((JsonNumber)v).GetInt32() <= 2);
+
+ Assert.Equal(1, jsonArray.Count);
+ Assert.Equal((JsonNumber)3, jsonArray[0]);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.Text.Json.Tests
+{
+ public static class JsonNodeCloneTests
+ {
+ [Fact]
+ public static void TestCloneJsonArray()
+ {
+ var jsonArray = new JsonArray { "value1", "value2" };
+ var jsonArrayCopy = jsonArray.Clone() as JsonArray;
+ Assert.Equal(2, jsonArrayCopy.Count);
+ jsonArray.Add("value3");
+ Assert.Equal(2, jsonArrayCopy.Count);
+ }
+
+ [Fact]
+ public static void TestDeepCloneJsonArray()
+ {
+ JsonArray inner = new JsonArray { 1, 2, 3 };
+ JsonArray outer = new JsonArray { inner };
+ JsonArray outerClone = (JsonArray)outer.Clone();
+ ((JsonArray) outerClone[0]).Add(4);
+
+ Assert.Equal(3, inner.Count);
+ }
+
+ [Fact]
+ public static void TestCloneJsonNode()
+ {
+ var jsonObject = new JsonObject
+ {
+ { "text", "property value" },
+ { "boolean", true },
+ { "number", 15 },
+ { "array", new JsonString[] { "value1", "value2"} }
+ };
+
+ var jsonObjectCopy = (JsonObject)jsonObject.Clone();
+
+ jsonObject["text"] = (JsonString)"something different";
+ Assert.Equal("property value", (JsonString)jsonObjectCopy["text"]);
+
+ ((JsonBoolean)jsonObject["boolean"]).Value = false;
+ Assert.True(((JsonBoolean)jsonObjectCopy["boolean"]).Value);
+
+ Assert.Equal(2, jsonObjectCopy.GetJsonArrayPropertyValue("array").Count);
+ jsonObject.GetJsonArrayPropertyValue("array").Add("value3");
+ Assert.Equal(2, jsonObjectCopy.GetJsonArrayPropertyValue("array").Count);
+
+ jsonObject.Add("new one", 123);
+ Assert.Equal(4, jsonObjectCopy.PropertyNames.Count);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.Text.Json.Tests
+{
+ public static class JsonNodeParseTests
+ {
+ private static string jsonSampleString = @"
+ {
+ ""text"": ""property value"",
+ ""boolean true"": true,
+ ""boolean false"": false,
+ ""null"": null,
+ ""int"": 17,
+ ""double"": 3.14,
+ ""scientific"": 3e100,
+ ""array"" : [1,2,3],
+ ""inner object"" :
+ {
+ ""inner property"" : ""value""
+ }
+ }";
+
+ [Fact]
+ public static void TestParseStringToJsonNode()
+ {
+ JsonNode node = JsonNode.Parse(jsonSampleString);
+
+ var jsonObject = (JsonObject)node;
+ Assert.Equal(9, jsonObject.PropertyNames.Count);
+ Assert.Equal(9, jsonObject.PropertyValues.Count);
+ Assert.Equal("property value", (JsonString)jsonObject["text"]);
+ Assert.True(((JsonBoolean)jsonObject["boolean true"]).Value);
+ Assert.False(((JsonBoolean)jsonObject["boolean false"]).Value);
+ Assert.Null(jsonObject["null"]);
+ Assert.Equal(17, ((JsonNumber)jsonObject["int"]).GetInt32());
+ Assert.Equal(3.14, ((JsonNumber)jsonObject["double"]).GetDouble());
+ Assert.Equal("3e100", ((JsonNumber)jsonObject["scientific"]).ToString());
+
+ var innerArray = (JsonArray)jsonObject["array"];
+ Assert.Equal(3, innerArray.Count);
+ Assert.Equal(1, ((JsonNumber)innerArray[0]).GetInt32());
+ Assert.Equal(2, ((JsonNumber)innerArray[1]).GetInt32());
+ Assert.Equal(3, ((JsonNumber)innerArray[2]).GetInt32());
+
+ var innerObject = (JsonObject)jsonObject["inner object"];
+ Assert.Equal(1, innerObject.PropertyNames.Count);
+ Assert.Equal(1, innerObject.PropertyValues.Count);
+ Assert.Equal("value", (JsonString)innerObject["inner property"]);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+
+namespace System.Text.Json.Tests
+{
+ /// <summary>
+ /// Helper class simulating external library
+ /// </summary>
+ internal static class EmployeesDatabase
+ {
+ private static int s_id = 0;
+ public static KeyValuePair<string, JsonNode> GetNextEmployee()
+ {
+ var employee = new JsonObject()
+ {
+ { "name", "John" } ,
+ { "surname", "Smith"},
+ { "age", 45 }
+ };
+
+ return new KeyValuePair<string, JsonNode>("employee" + s_id++, employee);
+ }
+
+ public static IEnumerable<KeyValuePair<string, JsonNode>> GetTenBestEmployees()
+ {
+ for (int i = 0; i < 10; i++)
+ yield return GetNextEmployee();
+ }
+
+ /// <summary>
+ /// Returns following JsonObject:
+ /// {
+ /// { "name" : "John" }
+ /// { "phone numbers" : { "work" : "425-555-0123", "home": "425-555-0134" } }
+ /// {
+ /// "reporting employees" :
+ /// {
+ /// "software developers" :
+ /// {
+ /// "full time employees" : /JsonObject of 3 employees fromk database/
+ /// "intern employees" : /JsonObject of 2 employees fromk database/
+ /// },
+ /// "HR" : /JsonObject of 10 employees fromk database/
+ /// }
+ /// </summary>
+ /// <returns></returns>
+ public static JsonObject GetManager()
+ {
+ var manager = GetNextEmployee().Value as JsonObject;
+
+ manager.Add
+ (
+ "phone numbers",
+ new JsonObject()
+ {
+ { "work", "425-555-0123" }, { "home", "425-555-0134" }
+ }
+ );
+
+ manager.Add
+ (
+ "reporting employees", new JsonObject()
+ {
+ {
+ "software developers", new JsonObject()
+ {
+ {
+ "full time employees", new JsonObject()
+ {
+ EmployeesDatabase.GetNextEmployee(),
+ EmployeesDatabase.GetNextEmployee(),
+ EmployeesDatabase.GetNextEmployee(),
+ }
+ },
+ {
+ "intern employees", new JsonObject()
+ {
+ EmployeesDatabase.GetNextEmployee(),
+ EmployeesDatabase.GetNextEmployee(),
+ }
+ }
+ }
+ },
+ {
+ "HR", new JsonObject()
+ {
+ {
+ "full time employees", new JsonObject(EmployeesDatabase.GetTenBestEmployees())
+ }
+ }
+ }
+ }
+ );
+
+ return manager;
+ }
+ }
+
+ /// <summary>
+ /// Helper class simulating enum
+ /// </summary>
+ internal enum AvailableStateCodes
+ {
+ WA,
+ CA,
+ NY,
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections;
+using Xunit;
+
+namespace System.Text.Json.Tests
+{
+ public static class JsonNodeToJsonElementTests
+ {
+ [Fact]
+ public static void TestAsJsonElement()
+ {
+ var jsonObject = new JsonObject
+ {
+ { "text", "property value" },
+ { "boolean", true },
+ { "number", 15 },
+ { "null node", (JsonNode) null },
+ { "array", new JsonString[] { "value1", "value2"} }
+ };
+
+ JsonElement jsonElement = jsonObject.AsJsonElement();
+ Assert.False(jsonElement.IsImmutable);
+
+ JsonElement.ObjectEnumerator enumerator = jsonElement.EnumerateObject();
+
+ enumerator.MoveNext();
+ Assert.Equal("text", enumerator.Current.Name);
+ Assert.Equal(JsonValueKind.String, enumerator.Current.Value.ValueKind);
+ Assert.Equal("property value", enumerator.Current.Value.GetString());
+
+ enumerator.MoveNext();
+ Assert.Equal("boolean", enumerator.Current.Name);
+ Assert.Equal(JsonValueKind.True, enumerator.Current.Value.ValueKind);
+ Assert.True(enumerator.Current.Value.GetBoolean());
+
+ enumerator.MoveNext();
+ Assert.Equal("number", enumerator.Current.Name);
+ Assert.Equal(15, enumerator.Current.Value.GetInt32());
+ Assert.Equal(JsonValueKind.Number, enumerator.Current.Value.ValueKind);
+
+ enumerator.MoveNext();
+ Assert.Equal("null node", enumerator.Current.Name);
+ Assert.Equal(JsonValueKind.Null, enumerator.Current.Value.ValueKind);
+
+ enumerator.MoveNext();
+ Assert.Equal("array", enumerator.Current.Name);
+ Assert.Equal(2, enumerator.Current.Value.GetArrayLength());
+ Assert.Equal(JsonValueKind.Array, enumerator.Current.Value.ValueKind);
+ JsonElement.ArrayEnumerator innerEnumerator = enumerator.Current.Value.EnumerateArray();
+
+ innerEnumerator.MoveNext();
+ Assert.Equal(JsonValueKind.String, innerEnumerator.Current.ValueKind);
+ Assert.Equal("value1", innerEnumerator.Current.GetString());
+
+ innerEnumerator.MoveNext();
+ Assert.Equal(JsonValueKind.String, innerEnumerator.Current.ValueKind);
+ Assert.Equal("value2", innerEnumerator.Current.GetString());
+
+ Assert.False(innerEnumerator.MoveNext());
+ innerEnumerator.Dispose();
+
+ Assert.False(enumerator.MoveNext());
+ enumerator.Dispose();
+
+ // Modifying JsonObject will change JsonElement:
+
+ jsonObject["text"] = new JsonNumber("123");
+ Assert.Equal(123, jsonElement.GetProperty("text").GetInt32());
+ }
+
+ [Fact]
+ public static void TestArrayIterator()
+ {
+ JsonArray array = new JsonArray { 1, 2, 3 };
+ JsonElement jsonNodeElement = array.AsJsonElement();
+ IEnumerator enumerator = jsonNodeElement.EnumerateArray();
+ array.Add(4);
+ Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
+ }
+
+ [Fact]
+ public static void TestGetNode()
+ {
+ var jsonObject = new JsonObject
+ {
+ { "text", "property value" },
+ { "boolean", true },
+ { "number", 15 },
+ { "null node", (JsonNode) null },
+ { "array", new JsonString[] { "value1", "value2"} }
+ };
+
+ JsonElement jsonElement = jsonObject.AsJsonElement();
+ JsonObject jsonObjectFromElement = (JsonObject)JsonNode.GetNode(jsonElement);
+
+ Assert.Equal("property value", (JsonString)jsonObjectFromElement["text"]);
+
+ // Modifying JsonObject will change JsonObjectFromElement:
+
+ jsonObject["text"] = new JsonString("something different");
+ Assert.Equal("something different", (JsonString)jsonObjectFromElement["text"]);
+
+ // Modifying JsonObjectFromElement will change JsonObject:
+
+ ((JsonBoolean)jsonObjectFromElement["boolean"]).Value = false;
+ Assert.False(((JsonBoolean)jsonObject["boolean"]).Value);
+ }
+ }
+}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Collections.Generic;
-
-namespace System.Text.Json.Tests
-{
- public static partial class JsonObjectTests
- {
- /// <summary>
- /// Helper class simulating external library
- /// </summary>
- private static class EmployeesDatabase
- {
- private static int s_id = 0;
- public static KeyValuePair<string, JsonNode> GetNextEmployee()
- {
- var employee = new JsonObject()
- {
- { "name", "John" } ,
- { "surname", "Smith"},
- { "age", 45 }
- };
-
- return new KeyValuePair<string, JsonNode>("employee" + s_id++, employee);
- }
-
- public static IEnumerable<KeyValuePair<string, JsonNode>> GetTenBestEmployees()
- {
- for (int i = 0; i < 10; i++)
- yield return GetNextEmployee();
- }
-
- /// <summary>
- /// Returns following JsonObject:
- /// {
- /// { "name" : "John" }
- /// { "phone numbers" : { "work" : "123-456-7890", "home": "123-456-7890" } }
- /// {
- /// "reporting employees" :
- /// {
- /// "software developers" :
- /// {
- /// "full time employees" : /JsonObject of 3 employees fromk database/
- /// "intern employees" : /JsonObject of 2 employees fromk database/
- /// },
- /// "HR" : /JsonObject of 10 employees fromk database/
- /// }
- /// </summary>
- /// <returns></returns>
- public static JsonObject GetManager()
- {
- var manager = GetNextEmployee().Value as JsonObject;
-
- manager.Add
- (
- "phone numbers",
- new JsonObject()
- {
- { "work", "123-456-7890" }, { "home", "123-456-7890" }
- }
- );
-
- manager.Add
- (
- "reporting employees", new JsonObject()
- {
- {
- "software developers", new JsonObject()
- {
- {
- "full time employees", new JsonObject()
- {
- EmployeesDatabase.GetNextEmployee(),
- EmployeesDatabase.GetNextEmployee(),
- EmployeesDatabase.GetNextEmployee(),
- }
- },
- {
- "intern employees", new JsonObject()
- {
- EmployeesDatabase.GetNextEmployee(),
- EmployeesDatabase.GetNextEmployee(),
- }
- }
- }
- },
- {
- "HR", new JsonObject()
- {
- {
- "full time employees", new JsonObject(EmployeesDatabase.GetTenBestEmployees())
- }
- }
- }
- }
- );
-
- return manager;
- }
- public static void PerformHeavyOperations(JsonElement employee) { }
- }
-
- private static class HealthCare
- {
- public static void CreateMedicalAppointment(string personName) { }
- }
-
- /// <summary>
- /// Helper class simulating enum
- /// </summary>
- private enum AvailableStateCodes
- {
- WA,
- CA,
- NY,
- }
- }
-}
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
using Xunit;
namespace System.Text.Json.Tests
{
- public static partial class JsonObjectTests
+ public static class JsonObjectTests
{
[Fact]
public static void TestDefaultConstructor()
Assert.Equal(guidString, (JsonString)jsonObject["guid"]);
}
- [Fact]
- public static void TestDateTime()
+ public static IEnumerable<object[]> DateTimeData =>
+ new List<object[]>
+ {
+ new object[] { new DateTime(DateTime.MinValue.Ticks, DateTimeKind.Utc) },
+ new object[] { new DateTime(2019, 1, 1) },
+ new object[] { new DateTime(2019, 1, 1, new GregorianCalendar()) },
+ new object[] { new DateTime(2019, 1, 1, new ChineseLunisolarCalendar()) }
+ };
+
+ [Theory]
+ [MemberData(nameof(DateTimeData))]
+ public static void TestDateTime(DateTime dateTime)
{
- DateTime dateTime = new DateTime(DateTime.MinValue.Ticks);
var jsonObject = new JsonObject { { "dateTime", dateTime } };
- Assert.Equal(dateTime.ToString(), (JsonString)jsonObject["dateTime"]);
+ Assert.Equal(dateTime.ToString("s", CultureInfo.InvariantCulture), (JsonString)jsonObject["dateTime"]);
}
- [Fact]
- public static void TestDateTimeOffset()
+ [Theory]
+ [MemberData(nameof(DateTimeData))]
+ public static void TestDateTimeOffset(DateTimeOffset dateTimeOffset)
{
- DateTimeOffset dateTimeOffset = new DateTime(DateTime.MinValue.Ticks, DateTimeKind.Utc);
var jsonObject = new JsonObject { { "dateTimeOffset", dateTimeOffset } };
- Assert.Equal(dateTimeOffset.ToString(), (JsonString)jsonObject["dateTimeOffset"]);
+ Assert.Equal(dateTimeOffset.ToString("s", CultureInfo.InvariantCulture), (JsonString)jsonObject["dateTimeOffset"]);
}
[Fact]
}
[Fact]
+ public static void TestAddingJsonArrayFromJsonNumberArray()
+ {
+ var preferences = new JsonObject()
+ {
+ { "prime numbers", new JsonNumber[] { 19, 37 } }
+ };
+
+ var primeNumbers = (JsonArray)preferences["prime numbers"];
+ Assert.Equal(2, primeNumbers.Count);
+
+ int[] expected = { 19, 37 };
+
+ for (int i = 0; i < primeNumbers.Count; i++)
+ {
+ Assert.IsType<JsonNumber>(primeNumbers[i]);
+ Assert.Equal(expected[i], primeNumbers[i] as JsonNumber);
+ }
+ }
+
+ [Fact]
+ public static void TestAddingJsonArray()
+ {
+ var preferences = new JsonObject()
+ {
+ { "colours", (JsonNode) new JsonArray{ "red", "green", "blue" } }
+ };
+
+ var colours = (JsonArray)preferences["colours"];
+ Assert.Equal(3, colours.Count);
+
+ string[] expected = { "red", "green", "blue" };
+
+ for (int i = 0; i < colours.Count; i++)
+ {
+ Assert.IsType<JsonString>(colours[i]);
+ Assert.Equal(expected[i], colours[i] as JsonString);
+ }
+ }
+
+ [Fact]
+ public static void TestAddingJsonArrayFromIEnumerableOfStrings()
+ {
+ var sportsExperienceYears = new JsonObject()
+ {
+ { "skiing", 5 },
+ { "cycling", 8 },
+ { "hiking", 6 },
+ { "chess", 2 },
+ { "skating", 1 },
+ };
+
+ // choose only sports with > 2 experience years
+ IEnumerable<string> sports = sportsExperienceYears.Where(sport => ((JsonNumber)sport.Value).GetInt32() > 2).Select(sport => sport.Key);
+
+ var preferences = new JsonObject()
+ {
+ { "sports", (JsonNode) new JsonArray(sports) }
+ };
+
+ var sportsJsonArray = (JsonArray)preferences["sports"];
+ Assert.Equal(3, sportsJsonArray.Count);
+
+ for (int i = 0; i < sportsJsonArray.Count; i++)
+ {
+ Assert.IsType<JsonString>(sportsJsonArray[i]);
+ Assert.Equal(sports.ElementAt(i), sportsJsonArray[i] as JsonString);
+ }
+ }
+
+ [Fact]
+ public static void TestAddingJsonArrayFromIEnumerableOfJsonNodes()
+ {
+ var strangeWords = new JsonArray()
+ {
+ "supercalifragilisticexpialidocious",
+ "gladiolus",
+ "albumen",
+ "smaragdine"
+ };
+
+ var preferences = new JsonObject()
+ {
+ { "strange words", strangeWords.Where(word => ((JsonString)word).Value.Length < 10) }
+ };
+
+ var strangeWordsJsonArray = (JsonArray)preferences["strange words"];
+ Assert.Equal(2, strangeWordsJsonArray.Count);
+
+ string[] expected = { "gladiolus", "albumen" };
+
+ for (int i = 0; i < strangeWordsJsonArray.Count; i++)
+ {
+ Assert.IsType<JsonString>(strangeWordsJsonArray[i]);
+ Assert.Equal(expected[i], strangeWordsJsonArray[i] as JsonString);
+ }
+ }
+
+ [Fact]
public static void TestContains()
{
var person = new JsonObject
[Fact]
public static void TestGetJsonObjectPropertyThrows()
{
- Assert.Throws<InvalidCastException>(() =>
+ Assert.Throws<ArgumentException>(() =>
{
var jsonObject = new JsonObject()
{
}
[Fact]
+ public static void TestGetJsonArrayPropertyThrows()
+ {
+ Assert.Throws<ArgumentException>(() =>
+ {
+ var jsonObject = new JsonObject()
+ {
+ { "name", "value" }
+ };
+
+ jsonObject.GetJsonArrayPropertyValue("name");
+ });
+ }
+
+ [Fact]
+ public static void TestTryGetArrayPropertyFails()
+ {
+ var jsonObject = new JsonObject()
+ {
+ { "name", "value" }
+ };
+
+ Assert.False(jsonObject.TryGetJsonArrayPropertyValue("name", out JsonArray property));
+ Assert.Null(property);
+
+ Assert.False(jsonObject.TryGetJsonArrayPropertyValue("other", out property));
+ Assert.Null(property);
+ }
+
+ [Fact]
public static void TestArgumentNullValidation()
{
Assert.Throws<ArgumentNullException>(() => new JsonObject().Add(null, ""));
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Collections.Generic;
+using System.Globalization;
using Xunit;
namespace System.Text.Json.Tests
Assert.Equal(guidString, jsonString);
}
- [Fact]
- public static void TestDateTime()
+ public static IEnumerable<object[]> DateTimeData =>
+ new List<object[]>
+ {
+ new object[] { new DateTime(DateTime.MinValue.Ticks, DateTimeKind.Utc) },
+ new object[] { new DateTime(2019, 1, 1) },
+ new object[] { new DateTime(2019, 1, 1, new GregorianCalendar()) },
+ new object[] { new DateTime(2019, 1, 1, new ChineseLunisolarCalendar()) }
+ };
+
+ [Theory]
+ [MemberData(nameof(DateTimeData))]
+ public static void TestDateTime(DateTime dateTime)
{
- DateTime dateTime = new DateTime(DateTime.MinValue.Ticks);
var jsonString = new JsonString(dateTime);
- Assert.Equal(dateTime.ToString(), jsonString);
+ Assert.Equal(dateTime.ToString("s", CultureInfo.InvariantCulture), jsonString);
}
- [Fact]
- public static void TestDateTimeOffset()
+ [Theory]
+ [MemberData(nameof(DateTimeData))]
+ public static void TestDateTimeOffset(DateTimeOffset dateTimeOffset)
{
- DateTimeOffset dateTimeOffset = new DateTime(DateTime.MinValue.Ticks, DateTimeKind.Utc);
var jsonString = new JsonString(dateTimeOffset);
- Assert.Equal(dateTimeOffset.ToString(), jsonString);
+ Assert.Equal(dateTimeOffset.ToString("s", CultureInfo.InvariantCulture), jsonString);
}
[Fact]
<Compile Include="JsonElementWriteTests.cs" />
<Compile Include="JsonEncodedTextTests.cs" />
<Compile Include="JsonGuidTestData.cs" />
+ <Compile Include="JsonNodeToJsonElementTests.cs" />
+ <Compile Include="JsonNodeCloneTests.cs" />
<Compile Include="JsonNumberTestData.cs" />
<Compile Include="JsonPropertyTests.cs" />
<Compile Include="JsonReaderStateAndOptionsTests.cs" />
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="JsonArrayTests.cs" />
<Compile Include="JsonBooleanTests.cs" />
+ <Compile Include="JsonNodeTestData.cs" />
+ <Compile Include="JsonNodeParseTests.cs" />
<Compile Include="JsonNumberTests.cs" />
<Compile Include="JsonObjectTests.cs" />
- <Compile Include="JsonObjectTests.TestData.cs" />
<Compile Include="JsonStringTests.cs" />
</ItemGroup>
</Project>