Skip to content

API for writeable DOM and C# dynamic support #47649

@steveharter

Description

@steveharter

Background and Motivation

See dotnet/designs#163. This contains various background and API details.

Proposed API

All types live in the existing System.Text.Json assembly.

The raw data from the "ref\System.Text.Json.cs" file:

namespace System.Text.Json.Node
{
    public sealed partial class JsonArray : JsonNode, ICollection<JsonNode?>, IEnumerable<JsonNode?>, IList<JsonNode?>, IEnumerable
    {
        public JsonArray(JsonNodeOptions? options = default(JsonNodeOptions?));
        public JsonArray(JsonNodeOptions options, params JsonNode?[] items);
        public JsonArray(params JsonNode?[] items);
        public int Count { get; }
        public static JsonArray? Create(JsonElement element, JsonNodeOptions? options = default(JsonNodeOptions?));
        bool ICollection<JsonNode?>.IsReadOnly { get; }
        public void Add(JsonNode? item);
        public void Add<T>(T value);
        public void Clear();
        public bool Contains(JsonNode? item);
        public IEnumerator<JsonNode?> GetEnumerator();
        public int IndexOf(JsonNode? item);
        public void Insert(int index, JsonNode? item);
        public bool Remove(JsonNode? item);
        public void RemoveAt(int index);
        void ICollection<JsonNode?>.CopyTo(JsonNode?[]? array, int arrayIndex);
        IEnumerator IEnumerable.GetEnumerator();
        public override void WriteTo(Utf8JsonWriter writer, JsonSerializerOptions? options = null);
    }
    public abstract partial class JsonNode
    {
        internal JsonNode();
        public JsonNode? this[int index] { get; set; }
        public JsonNode? this[string propertyName] { get; set; }
        public JsonNodeOptions? Options { get; }
        public JsonNode? Parent { get; }
        public JsonNode Root { get; }
        public JsonArray AsArray();
        public JsonObject AsObject();
        public JsonValue AsValue();
        public string GetPath();
        public virtual TValue GetValue<[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)]TValue>();
        public static explicit operator bool(JsonNode value);
        public static explicit operator byte(JsonNode value);
        public static explicit operator char(JsonNode value);
        public static explicit operator DateTime(JsonNode value);
        public static explicit operator DateTimeOffset(JsonNode value);
        public static explicit operator decimal(JsonNode value);
        public static explicit operator double(JsonNode value);
        public static explicit operator Guid(JsonNode value);
        public static explicit operator short(JsonNode value);
        public static explicit operator int(JsonNode value);
        public static explicit operator long(JsonNode value);
        public static explicit operator bool?(JsonNode? value);
        public static explicit operator byte?(JsonNode? value);
        public static explicit operator char?(JsonNode? value);
        public static explicit operator DateTimeOffset?(JsonNode? value);
        public static explicit operator DateTime?(JsonNode? value);
        public static explicit operator decimal?(JsonNode? value);
        public static explicit operator double?(JsonNode? value);
        public static explicit operator Guid?(JsonNode? value);
        public static explicit operator short?(JsonNode? value);
        public static explicit operator int?(JsonNode? value);
        public static explicit operator long?(JsonNode? value);
        [CLSCompliantAttribute(false)]
        public static explicit operator sbyte?(JsonNode? value);
        public static explicit operator float?(JsonNode? value);
        [CLSCompliantAttribute(false)]
        public static explicit operator ushort?(JsonNode? value);
        [CLSCompliantAttribute(false)]
        public static explicit operator uint?(JsonNode? value);
        [CLSCompliantAttribute(false)]
        public static explicit operator ulong?(JsonNode? value);
        [CLSCompliantAttribute(false)]
        public static explicit operator sbyte(JsonNode value);
        public static explicit operator float(JsonNode value);
        public static explicit operator string?(JsonNode? value);
        [CLSCompliantAttribute(false)]
        public static explicit operator ushort(JsonNode value);
        [CLSCompliantAttribute(false)]
        public static explicit operator uint(JsonNode value);
        [CLSCompliantAttribute(false)]
        public static explicit operator ulong(JsonNode value);
        public static implicit operator JsonNode(bool value);
        public static implicit operator JsonNode(byte value);
        public static implicit operator JsonNode(char value);
        public static implicit operator JsonNode(DateTime value);
        public static implicit operator JsonNode(DateTimeOffset value);
        public static implicit operator JsonNode(decimal value);
        public static implicit operator JsonNode(double value);
        public static implicit operator JsonNode(Guid value);
        public static implicit operator JsonNode(short value);
        public static implicit operator JsonNode(int value);
        public static implicit operator JsonNode(long value);
        public static implicit operator JsonNode?(bool? value);
        public static implicit operator JsonNode?(byte? value);
        public static implicit operator JsonNode?(char? value);
        public static implicit operator JsonNode?(DateTimeOffset? value);
        public static implicit operator JsonNode?(DateTime? value);
        public static implicit operator JsonNode?(decimal? value);
        public static implicit operator JsonNode?(double? value);
        public static implicit operator JsonNode?(Guid? value);
        public static implicit operator JsonNode?(short? value);
        public static implicit operator JsonNode?(int? value);
        public static implicit operator JsonNode?(long? value);
        [CLSCompliantAttribute(false)]
        public static implicit operator JsonNode?(sbyte? value);
        public static implicit operator JsonNode?(float? value);
        [CLSCompliantAttribute(false)]
        public static implicit operator JsonNode?(ushort? value);
        [CLSCompliantAttribute(false)]
        public static implicit operator JsonNode?(uint? value);
        [CLSCompliantAttribute(false)]
        public static implicit operator JsonNode?(ulong? value);
        [CLSCompliantAttribute(false)]
        public static implicit operator JsonNode(sbyte value);
        public static implicit operator JsonNode(float value);
        public static implicit operator JsonNode?(string? value);
        [CLSCompliantAttribute(false)]
        public static implicit operator JsonNode(ushort value);
        [CLSCompliantAttribute(false)]
        public static implicit operator JsonNode(uint value);
        [CLSCompliantAttribute(false)]
        public static implicit operator JsonNode(ulong value);
        public static JsonNode? Parse(string json, JsonNodeOptions? nodeOptions = default(JsonNodeOptions?), JsonDocumentOptions documentOptions = default(JsonDocumentOptions));
        public static JsonNode? Parse(ref Utf8JsonReader reader, JsonNodeOptions? nodeOptions = default(JsonNodeOptions?));
        public static JsonNode? Parse(IO.Stream utf8Json, JsonNodeOptions? nodeOptions = default(JsonNodeOptions?), JsonDocumentOptions documentOptions = default(JsonDocumentOptions));
        public static JsonNode? Parse(ReadOnlySpan<byte> utf8Json, JsonNodeOptions? nodeOptions = default(JsonNodeOptions?), JsonDocumentOptions documentOptions = default(JsonDocumentOptions));
        public string ToJsonString(JsonSerializerOptions? options = null);
        public override string ToString();
        public abstract void WriteTo(Utf8JsonWriter writer, JsonSerializerOptions? options = null);
    }
    public partial struct JsonNodeOptions
    {
        private int _dummyPrimitive;
        public bool PropertyNameCaseInsensitive { readonly get; set; }
    }
    public sealed partial class JsonObject : JsonNode, ICollection<KeyValuePair<string, JsonNode?>>, IDictionary<string, JsonNode?>, IEnumerable<KeyValuePair<string, JsonNode?>>, IEnumerable
    {
        public JsonObject(JsonNodeOptions? options = default(JsonNodeOptions?));
        public JsonObject(IEnumerable<KeyValuePair<string, JsonNode?>> properties, JsonNodeOptions? options = default(JsonNodeOptions?));
        public int Count { get; }
        public static JsonObject? Create(JsonElement element, JsonNodeOptions? options = default(JsonNodeOptions?));
        bool ICollection<KeyValuePair<string, JsonNode?>>.IsReadOnly { get; }
        ICollection<string> IDictionary<string, JsonNode?>.Keys { get; }
        ICollection<JsonNode?> IDictionary<string, JsonNode?>.Values { get; }
        public void Add(KeyValuePair<string, JsonNode?> property);
        public void Add(string propertyName, JsonNode? value);
        public void Clear();
        public bool ContainsKey(string propertyName);
        public IEnumerator<KeyValuePair<string, JsonNode?>> GetEnumerator();
        public bool Remove(string propertyName);
        bool ICollection<KeyValuePair<string, JsonNode?>>.Contains(KeyValuePair<string, JsonNode> item);
        void ICollection<KeyValuePair<string, JsonNode?>>.CopyTo(KeyValuePair<string, JsonNode>[] array, int index);
        bool ICollection<KeyValuePair<string, JsonNode?>>.Remove(KeyValuePair<string, JsonNode> item);
        bool IDictionary<string, JsonNode?>.TryGetValue(string propertyName, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)]out JsonNode jsonNode);
        IEnumerator IEnumerable.GetEnumerator();
        public bool TryGetPropertyValue(string propertyName, out JsonNode? jsonNode);
        public override void WriteTo(Utf8JsonWriter writer, JsonSerializerOptions? options = null);
    }
    public abstract partial class JsonValue : JsonNode
    {
        private protected JsonValue(JsonNodeOptions? options = null);
        public static JsonValue? Create<T>(T value, JsonNodeOptions? options = default(JsonNodeOptions?));
        public abstract bool TryGetValue<[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)]T>([System.Diagnostics.CodeAnalysis.NotNullWhen(true)]out T? value);
    }
}

To support the dynamic keyword in C#, the "inbox" build will also contain:

    public partial class JsonNode : System.Dynamic.IDynamicMetaObjectProvider
    {
        System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression parameter) { throw null; }
    }

Non-inbox builds won't support for dynamic (at least for now, pending feedback) since it would require a new package reference to System.Linq.Expressions.

Additions to JsonSerializerOptions:

namespace System.Text.Json
{
    // Determines the type to create for extension data and properties declared as System.Object.
    public enum JsonUnknownTypeHandling
    {
        JsonElement = 0, // Default
        JsonNode = 1, // Create JsonNode*-derived types for System.Object and JsonObject for extension data.
    }

    public partial class JsonSerializerOptions
    {
        public JsonUnknownTypeHandling UnknownTypeHandling {get; set;}
    }
}

Usage Examples

See the referenced design above. Here's a simple deserialize and serialize example:

JsonObject jObject = JsonObject.Parse("{""MyProperty"":42}");
int i = (int) jObject["MyProperty"];
string json = jObject.ToJsonString();

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions