From 578adcd3f82b32e835e3cc13b941f73b595fe768 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Thu, 2 Feb 2023 14:48:16 -0800 Subject: [PATCH] Prototype moving JsonData to be backed by JsonDocument (#33063) * Add WriteTo(), ==operators, and PR FB * export API * Add DocumentSentiment large JSON sample and add parse and read benchmarks * initial attempt to move to JsonElement * some fixes * updates * nit * nits * starting to experiment with changelist approach * an approach to object assignment * start working with array elements * saving changes from before break... * sm formatting pr fb * ApiView FB per generality of DynamicData * nits * small thoughts * first steps toward abstracting ChangeTracker * In flight changes while implementing AddPropertyToObject * adding tests with simple modifications to structural elements * Implementing WriteTo() * Added nested objects * refactor * missed changes * In TDD spirit, add failing WriteTo test * Test passes * quick refactor * update add property test * Update WriteTo to handle property additions at the root element * Handle property additions on arbitrary objects * handle standard property removals * Support Replace with object * Support Replace with object * refactor where we serialize structural changes to centralize * Implement reference semantics for JsonDataElement * experiment with checking ancestors for structural changes. * Update WriteTo to handle structural changes. * add high water mark logic * add validation to all reads and add failing test for ignoring pre-structural change. * Incorporate HWM logic in all change lookups; make PriorChangeToReplacedPropertyIsIgnored * remove double-check of object and array elements * some tidy up * Reimplement GetProperty in terms of TryGetProperty() * Handle WriteTo for structural changes. * add some tests of structural changes * refactor tests * Bug fix to WriteTo for bools and test refactoring * bug fix to WriteTo for booleans * Add support for nulls and fix ToLower() bug * add perf benchmark prior to working on perf * missed file * update Parse() to use Memory * move subclasses to separate files for ease of reading * refactor to add dynamic layer * Add cast operators to JsonDataElement to pass dynamic GetIntProperty test * Add support for dynamic nested property access * enable set via dynamic layer * Enable setting nested properties via dynamic layer * nit * Renames prior to dynamic refactor * start adding separate dynamic layer * Move dynamic meta object to dyanmic types. * API updates * todos and nits * rename test to match types * remove outer DynamicJson class * add BindConvert to dynamic layer * Add BindGetIndex to dynamic layer * save multitarget attempt * Add target frameworks to Experimental to allow ifdefs by target framework. * PR feedback; use more efficient Deserialize call when available in ConvertTo() * refactor per PR fb * Add BindSetIndex to dynamic layer * Fix bug in mutable ToString() where changes to descendants weren't accounted for * Fix WriteTo() bug for string elements and add failing test for handling nulls * Handle null values * PR fb * Add support for floats and longs * remove HasValue, per pr fb * export API changes * Support adding properties on dynamic member assignments * reshuffle methods around * some changes before adding support for IEnumerable * Add ArrayEnumerator to MutableJsonElement * missed file; dynamic portion not complete * Bug fix to dynamic ArrayEnumerator; foreach over array now passes * export API and misc test updates * nit; cleanup * Make MutableJsonDocument serializable * Make DynamicJson serializable; enable reference semantics for added properties * Make tests pass for net6.0 & net7.0; will address net461 separately * Fixes for some net461 issues * Fix Add and Set property for net461 * Work around BindBinaryOperation in net461; move inline TODOs to GH issue * Export API --- .../Azure.Core.Experimental.netstandard2.0.cs | 123 ++- .../src/Azure.Core.Experimental.csproj | 4 +- .../src/BinaryDataExtensions.cs | 2 +- .../src/DynamicData.cs | 12 +- .../src/JsonData.Operators.cs | 405 ---------- .../Azure.Core.Experimental/src/JsonData.cs | 753 ------------------ .../src/Properties/AssemblyInfo.cs | 1 + .../tests/JsonDataDynamicMutableTests.cs | 82 +- .../tests/JsonDataTests.cs | 404 +++++----- .../tests/perf/DynamicReadingBenchmark.cs | 2 +- .../tests/perf/FirstReadBenchmark.cs | 2 +- .../tests/perf/Program.cs | 13 + .../tests/public/JsonDataArrayTests.cs | 18 +- .../tests/public/JsonDataLeafTests.cs | 8 +- .../tests/public/JsonDataObjectTests.cs | 1 + .../public/JsonDataPublicMutableTests.cs | 4 +- .../tests/public/JsonDataPublicTests.cs | 59 +- 17 files changed, 377 insertions(+), 1516 deletions(-) delete mode 100644 sdk/core/Azure.Core.Experimental/src/JsonData.Operators.cs delete mode 100644 sdk/core/Azure.Core.Experimental/src/JsonData.cs diff --git a/sdk/core/Azure.Core.Experimental/api/Azure.Core.Experimental.netstandard2.0.cs b/sdk/core/Azure.Core.Experimental/api/Azure.Core.Experimental.netstandard2.0.cs index 9540ae753f989..7335516eb5e0c 100644 --- a/sdk/core/Azure.Core.Experimental/api/Azure.Core.Experimental.netstandard2.0.cs +++ b/sdk/core/Azure.Core.Experimental/api/Azure.Core.Experimental.netstandard2.0.cs @@ -115,52 +115,89 @@ public static partial class BinaryDataExtensions public abstract partial class DynamicData { protected DynamicData() { } - internal abstract void WriteTo(System.Text.Json.Utf8JsonWriter writer); - public static void WriteTo(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Dynamic.DynamicData data) { } + internal abstract void WriteTo(System.IO.Stream stream); + public static void WriteTo(System.IO.Stream stream, Azure.Core.Dynamic.DynamicData data) { } } - [System.Diagnostics.DebuggerDisplayAttribute("{DebuggerDisplay,nq}")] - public partial class JsonData : Azure.Core.Dynamic.DynamicData, System.Dynamic.IDynamicMetaObjectProvider, System.IEquatable + public partial class DynamicJson : Azure.Core.Dynamic.DynamicData, System.Dynamic.IDynamicMetaObjectProvider { - internal JsonData() { } - public bool Equals(Azure.Core.Dynamic.JsonData other) { throw null; } - public override bool Equals(object? obj) { throw null; } - public override int GetHashCode() { throw null; } - public static bool operator ==(Azure.Core.Dynamic.JsonData? left, bool right) { throw null; } - public static bool operator ==(Azure.Core.Dynamic.JsonData? left, double right) { throw null; } - public static bool operator ==(Azure.Core.Dynamic.JsonData? left, int right) { throw null; } - public static bool operator ==(Azure.Core.Dynamic.JsonData? left, long right) { throw null; } - public static bool operator ==(Azure.Core.Dynamic.JsonData? left, float right) { throw null; } - public static bool operator ==(Azure.Core.Dynamic.JsonData? left, string? right) { throw null; } - public static bool operator ==(bool left, Azure.Core.Dynamic.JsonData? right) { throw null; } - public static bool operator ==(double left, Azure.Core.Dynamic.JsonData? right) { throw null; } - public static bool operator ==(int left, Azure.Core.Dynamic.JsonData? right) { throw null; } - public static bool operator ==(long left, Azure.Core.Dynamic.JsonData? right) { throw null; } - public static bool operator ==(float left, Azure.Core.Dynamic.JsonData? right) { throw null; } - public static bool operator ==(string? left, Azure.Core.Dynamic.JsonData? right) { throw null; } - public static implicit operator bool (Azure.Core.Dynamic.JsonData json) { throw null; } - public static implicit operator double (Azure.Core.Dynamic.JsonData json) { throw null; } - public static implicit operator int (Azure.Core.Dynamic.JsonData json) { throw null; } - public static implicit operator long (Azure.Core.Dynamic.JsonData json) { throw null; } - public static implicit operator bool? (Azure.Core.Dynamic.JsonData json) { throw null; } - public static implicit operator double? (Azure.Core.Dynamic.JsonData json) { throw null; } - public static implicit operator int? (Azure.Core.Dynamic.JsonData json) { throw null; } - public static implicit operator long? (Azure.Core.Dynamic.JsonData json) { throw null; } - public static implicit operator float? (Azure.Core.Dynamic.JsonData json) { throw null; } - public static implicit operator float (Azure.Core.Dynamic.JsonData json) { throw null; } - public static implicit operator string (Azure.Core.Dynamic.JsonData json) { throw null; } - public static bool operator !=(Azure.Core.Dynamic.JsonData? left, bool right) { throw null; } - public static bool operator !=(Azure.Core.Dynamic.JsonData? left, double right) { throw null; } - public static bool operator !=(Azure.Core.Dynamic.JsonData? left, int right) { throw null; } - public static bool operator !=(Azure.Core.Dynamic.JsonData? left, long right) { throw null; } - public static bool operator !=(Azure.Core.Dynamic.JsonData? left, float right) { throw null; } - public static bool operator !=(Azure.Core.Dynamic.JsonData? left, string? right) { throw null; } - public static bool operator !=(bool left, Azure.Core.Dynamic.JsonData? right) { throw null; } - public static bool operator !=(double left, Azure.Core.Dynamic.JsonData? right) { throw null; } - public static bool operator !=(int left, Azure.Core.Dynamic.JsonData? right) { throw null; } - public static bool operator !=(long left, Azure.Core.Dynamic.JsonData? right) { throw null; } - public static bool operator !=(float left, Azure.Core.Dynamic.JsonData? right) { throw null; } - public static bool operator !=(string? left, Azure.Core.Dynamic.JsonData? right) { throw null; } + internal DynamicJson() { } + public static implicit operator bool (Azure.Core.Dynamic.DynamicJson value) { throw null; } + public static implicit operator double (Azure.Core.Dynamic.DynamicJson value) { throw null; } + public static implicit operator int (Azure.Core.Dynamic.DynamicJson value) { throw null; } + public static implicit operator long (Azure.Core.Dynamic.DynamicJson value) { throw null; } + public static implicit operator bool? (Azure.Core.Dynamic.DynamicJson value) { throw null; } + public static implicit operator double? (Azure.Core.Dynamic.DynamicJson value) { throw null; } + public static implicit operator int? (Azure.Core.Dynamic.DynamicJson value) { throw null; } + public static implicit operator long? (Azure.Core.Dynamic.DynamicJson value) { throw null; } + public static implicit operator float? (Azure.Core.Dynamic.DynamicJson value) { throw null; } + public static implicit operator float (Azure.Core.Dynamic.DynamicJson value) { throw null; } + public static implicit operator string (Azure.Core.Dynamic.DynamicJson value) { throw null; } System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression parameter) { throw null; } public override string ToString() { throw null; } + [System.Diagnostics.DebuggerDisplayAttribute("{Current,nq}")] + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public partial struct ArrayEnumerator : System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerator, System.Collections.IEnumerable, System.Collections.IEnumerator, System.IDisposable + { + private object _dummy; + private int _dummyPrimitive; + public Azure.Core.Dynamic.DynamicJson Current { get { throw null; } } + object System.Collections.IEnumerator.Current { get { throw null; } } + public void Dispose() { } + public Azure.Core.Dynamic.DynamicJson.ArrayEnumerator GetEnumerator() { throw null; } + public bool MoveNext() { throw null; } + public void Reset() { } + System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() { throw null; } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + } + } + public partial class MutableJsonDocument + { + internal MutableJsonDocument() { } + public Azure.Core.Dynamic.MutableJsonElement RootElement { get { throw null; } } + public static Azure.Core.Dynamic.MutableJsonDocument Parse(System.BinaryData utf8Json) { throw null; } + public static Azure.Core.Dynamic.MutableJsonDocument Parse(string json) { throw null; } + public void WriteTo(System.IO.Stream stream, System.Buffers.StandardFormat format = default(System.Buffers.StandardFormat)) { } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public partial struct MutableJsonElement + { + private object _dummy; + private int _dummyPrimitive; + public System.Text.Json.JsonValueKind ValueKind { get { throw null; } } + public Azure.Core.Dynamic.MutableJsonElement.ArrayEnumerator EnumerateArray() { throw null; } + public bool GetBoolean() { throw null; } + public double GetDouble() { throw null; } + public int GetInt32() { throw null; } + public long GetInt64() { throw null; } + public Azure.Core.Dynamic.MutableJsonElement GetProperty(string name) { throw null; } + public float GetSingle() { throw null; } + public string? GetString() { throw null; } + public void RemoveProperty(string name) { } + public void Set(Azure.Core.Dynamic.MutableJsonElement value) { } + public void Set(bool value) { } + public void Set(double value) { } + public void Set(int value) { } + public void Set(long value) { } + public void Set(object value) { } + public void Set(float value) { } + public void Set(string value) { } + public Azure.Core.Dynamic.MutableJsonElement SetProperty(string name, object value) { throw null; } + public override string ToString() { throw null; } + public bool TryGetProperty(string name, out Azure.Core.Dynamic.MutableJsonElement value) { throw null; } + [System.Diagnostics.DebuggerDisplayAttribute("{Current,nq}")] + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public partial struct ArrayEnumerator : System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerator, System.Collections.IEnumerable, System.Collections.IEnumerator, System.IDisposable + { + private object _dummy; + private int _dummyPrimitive; + public Azure.Core.Dynamic.MutableJsonElement Current { get { throw null; } } + object System.Collections.IEnumerator.Current { get { throw null; } } + public void Dispose() { } + public Azure.Core.Dynamic.MutableJsonElement.ArrayEnumerator GetEnumerator() { throw null; } + public bool MoveNext() { throw null; } + public void Reset() { } + System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() { throw null; } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + } } } diff --git a/sdk/core/Azure.Core.Experimental/src/Azure.Core.Experimental.csproj b/sdk/core/Azure.Core.Experimental/src/Azure.Core.Experimental.csproj index f1059fb1bbd90..b45eb4a93d693 100644 --- a/sdk/core/Azure.Core.Experimental/src/Azure.Core.Experimental.csproj +++ b/sdk/core/Azure.Core.Experimental/src/Azure.Core.Experimental.csproj @@ -1,11 +1,11 @@ - + Experimental types that might eventually move to Azure.Core Microsoft Azure Client Pipeline Experimental Extensions 0.1.0-preview.24 Microsoft Azure Client Pipeline enable - $(RequiredTargetFrameworks) + $(RequiredTargetFrameworks);net461;net6.0 $(NoWarn);AZC0001;AZC0012 true diff --git a/sdk/core/Azure.Core.Experimental/src/BinaryDataExtensions.cs b/sdk/core/Azure.Core.Experimental/src/BinaryDataExtensions.cs index 355d8deac7a75..f34689fa4365c 100644 --- a/sdk/core/Azure.Core.Experimental/src/BinaryDataExtensions.cs +++ b/sdk/core/Azure.Core.Experimental/src/BinaryDataExtensions.cs @@ -15,7 +15,7 @@ public static class BinaryDataExtensions /// public static dynamic ToDynamic(this BinaryData data) { - return JsonData.Parse(data); + return new DynamicJson(MutableJsonDocument.Parse(data).RootElement); } } } diff --git a/sdk/core/Azure.Core.Experimental/src/DynamicData.cs b/sdk/core/Azure.Core.Experimental/src/DynamicData.cs index 9b3c64b6561f8..47d6f573bca10 100644 --- a/sdk/core/Azure.Core.Experimental/src/DynamicData.cs +++ b/sdk/core/Azure.Core.Experimental/src/DynamicData.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.Text.Json; +using System.IO; namespace Azure.Core.Dynamic { @@ -14,15 +14,15 @@ namespace Azure.Core.Dynamic public abstract class DynamicData { /// - /// Writes the data to the provided writer as a JSON value. + /// Writes the data to the provided stream. /// - /// The writer to which to write the document. + /// The stream to which to write the document. /// The dynamic data value to write. - public static void WriteTo(Utf8JsonWriter writer, DynamicData data) + public static void WriteTo(Stream stream, DynamicData data) { - data.WriteTo(writer); + data.WriteTo(stream); } - internal abstract void WriteTo(Utf8JsonWriter writer); + internal abstract void WriteTo(Stream stream); } } diff --git a/sdk/core/Azure.Core.Experimental/src/JsonData.Operators.cs b/sdk/core/Azure.Core.Experimental/src/JsonData.Operators.cs deleted file mode 100644 index 817a7c544bb3c..0000000000000 --- a/sdk/core/Azure.Core.Experimental/src/JsonData.Operators.cs +++ /dev/null @@ -1,405 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Dynamic; -using System.Text.Json; - -namespace Azure.Core.Dynamic -{ - /// - /// A mutable representation of a JSON value. - /// - public partial class JsonData : IDynamicMetaObjectProvider, IEquatable - { - /// - /// Converts the value to a - /// - /// The value to convert. - public static implicit operator bool(JsonData json) => json.GetBoolean(); - - /// - /// Converts the value to a - /// - /// The value to convert. - public static implicit operator int(JsonData json) => json.GetInt32(); - - /// - /// Converts the value to a - /// - /// The value to convert. - public static implicit operator long(JsonData json) => json.GetLong(); - - /// - /// Converts the value to a - /// - /// The value to convert. - public static implicit operator string?(JsonData json) => json.GetString(); - - /// - /// Converts the value to a - /// - /// The value to convert. - public static implicit operator float(JsonData json) => json.GetFloat(); - - /// - /// Converts the value to a - /// - /// The value to convert. - public static implicit operator double(JsonData json) => json.GetDouble(); - - /// - /// Converts the value to a or null. - /// - /// The value to convert. - public static implicit operator bool?(JsonData json) => json.Kind == JsonValueKind.Null ? null : json.GetBoolean(); - - /// - /// Converts the value to a or null. - /// - /// The value to convert. - public static implicit operator int?(JsonData json) => json.Kind == JsonValueKind.Null ? null : json.GetInt32(); - - /// - /// Converts the value to a or null. - /// - /// The value to convert. - public static implicit operator long?(JsonData json) => json.Kind == JsonValueKind.Null ? null : json.GetLong(); - - /// - /// Converts the value to a or null. - /// - /// The value to convert. - public static implicit operator float?(JsonData json) => json.Kind == JsonValueKind.Null ? null : json.GetFloat(); - - /// - /// Converts the value to a or null. - /// - /// The value to convert. - public static implicit operator double?(JsonData json) => json.Kind == JsonValueKind.Null ? null : json.GetDouble(); - - /// - /// Returns true if a has the same value as a given bool, - /// and false otherwise. - /// - /// The to compare. - /// The to compare. - /// True if the given JsonData represents the given bool, and false otherwise. - public static bool operator ==(JsonData? left, bool right) - { - if (left is null) - { - return false; - } - - return (left.Kind == JsonValueKind.False || left.Kind == JsonValueKind.True) && - ((bool)left) == right; - } - - /// - /// Returns false if a has the same value as a given bool, - /// and true otherwise. - /// - /// The to compare. - /// The to compare. - /// False if the given JsonData represents the given string, and false otherwise - public static bool operator !=(JsonData? left, bool right) => !(left == right); - - /// - /// Returns true if a has the same value as a given bool, - /// and false otherwise. - /// - /// The to compare. - /// The to compare. - /// True if the given JsonData represents the given bool, and false otherwise. - public static bool operator ==(bool left, JsonData? right) - { - if (right is null) - { - return false; - } - - return (right.Kind == JsonValueKind.False || right.Kind == JsonValueKind.True) && - ((bool)right) == left; - } - - /// - /// Returns false if a has the same value as a given bool, - /// and true otherwise. - /// - /// The to compare. - /// The to compare. - /// False if the given JsonData represents the given bool, and false otherwise - public static bool operator !=(bool left, JsonData? right) => !(left == right); - - /// - /// Returns true if a has the same value as a given int, - /// and false otherwise. - /// - /// The to compare. - /// The to compare. - /// True if the given JsonData represents the given int, and false otherwise. - public static bool operator ==(JsonData? left, int right) - { - if (left is null) - { - return false; - } - - return left.Kind == JsonValueKind.Number && ((int)left) == right; - } - - /// - /// Returns false if a has the same value as a given int, - /// and true otherwise. - /// - /// The to compare. - /// The to compare. - /// False if the given JsonData represents the given string, and false otherwise - public static bool operator !=(JsonData? left, int right) => !(left == right); - - /// - /// Returns true if a has the same value as a given int, - /// and false otherwise. - /// - /// The to compare. - /// The to compare. - /// True if the given JsonData represents the given int, and false otherwise. - public static bool operator ==(int left, JsonData? right) - { - if (right is null) - { - return false; - } - - return right.Kind == JsonValueKind.Number && ((int)right) == left; - } - - /// - /// Returns false if a has the same value as a given int, - /// and true otherwise. - /// - /// The to compare. - /// The to compare. - /// False if the given JsonData represents the given int, and false otherwise - public static bool operator !=(int left, JsonData? right) => !(left == right); - - /// - /// Returns true if a has the same value as a given long, - /// and false otherwise. - /// - /// The to compare. - /// The to compare. - /// True if the given JsonData represents the given long, and false otherwise. - public static bool operator ==(JsonData? left, long right) - { - if (left is null) - { - return false; - } - - return left.Kind == JsonValueKind.Number && ((long)left) == right; - } - - /// - /// Returns false if a has the same value as a given long, - /// and true otherwise. - /// - /// The to compare. - /// The to compare. - /// False if the given JsonData represents the given string, and false otherwise - public static bool operator !=(JsonData? left, long right) => !(left == right); - - /// - /// Returns true if a has the same value as a given long, - /// and false otherwise. - /// - /// The to compare. - /// The to compare. - /// True if the given JsonData represents the given long, and false otherwise. - public static bool operator ==(long left, JsonData? right) - { - if (right is null) - { - return false; - } - - return right.Kind == JsonValueKind.Number && ((long)right) == left; - } - - /// - /// Returns false if a has the same value as a given long, - /// and true otherwise. - /// - /// The to compare. - /// The to compare. - /// False if the given JsonData represents the given long, and false otherwise - public static bool operator !=(long left, JsonData? right) => !(left == right); - - /// - /// Returns true if a has the same value as a given string, - /// and false otherwise. - /// - /// The to compare. - /// The to compare. - /// True if the given JsonData represents the given string, and false otherwise. - public static bool operator ==(JsonData? left, string? right) - { - if (left is null && right is null) - { - return true; - } - - if (left is null || right is null) - { - return false; - } - - return left.Kind == JsonValueKind.String && ((string?)left._value) == right; - } - - /// - /// Returns false if a has the same value as a given string, - /// and true otherwise. - /// - /// The to compare. - /// The to compare. - /// False if the given JsonData represents the given string, and false otherwise - public static bool operator !=(JsonData? left, string? right) => !(left == right); - - /// - /// Returns true if a has the same value as a given string, - /// and false otherwise. - /// - /// The to compare. - /// The to compare. - /// True if the given JsonData represents the given string, and false otherwise. - public static bool operator ==(string? left, JsonData? right) - { - if (left is null && right is null) - { - return true; - } - - if (left is null || right is null) - { - return false; - } - - return right.Kind == JsonValueKind.String && ((string?)right._value) == left; - } - - /// - /// Returns false if a has the same value as a given string, - /// and true otherwise. - /// - /// The to compare. - /// The to compare. - /// False if the given JsonData represents the given string, and false otherwise - public static bool operator !=(string? left, JsonData? right) => !(left == right); - - /// - /// Returns true if a has the same value as a given float, - /// and false otherwise. - /// - /// The to compare. - /// The to compare. - /// True if the given JsonData represents the given float, and false otherwise. - public static bool operator ==(JsonData? left, float right) - { - if (left is null) - { - return false; - } - - return left.Kind == JsonValueKind.Number && ((float)left) == right; - } - - /// - /// Returns false if a has the same value as a given float, - /// and true otherwise. - /// - /// The to compare. - /// The to compare. - /// False if the given JsonData represents the given string, and false otherwise - public static bool operator !=(JsonData? left, float right) => !(left == right); - - /// - /// Returns true if a has the same value as a given float, - /// and false otherwise. - /// - /// The to compare. - /// The to compare. - /// True if the given JsonData represents the given float, and false otherwise. - public static bool operator ==(float left, JsonData? right) - { - if (right is null) - { - return false; - } - - return right.Kind == JsonValueKind.Number && ((float)right) == left; - } - - /// - /// Returns false if a has the same value as a given float, - /// and true otherwise. - /// - /// The to compare. - /// The to compare. - /// False if the given JsonData represents the given float, and false otherwise - public static bool operator !=(float left, JsonData? right) => !(left == right); - - /// - /// Returns true if a has the same value as a given double, - /// and false otherwise. - /// - /// The to compare. - /// The to compare. - /// True if the given JsonData represents the given double, and false otherwise. - public static bool operator ==(JsonData? left, double right) - { - if (left is null) - { - return false; - } - - return left.Kind == JsonValueKind.Number && ((double)left) == right; - } - - /// - /// Returns false if a has the same value as a given double, - /// and true otherwise. - /// - /// The to compare. - /// The to compare. - /// False if the given JsonData represents the given string, and false otherwise - public static bool operator !=(JsonData? left, double right) => !(left == right); - - /// - /// Returns true if a has the same value as a given double, - /// and false otherwise. - /// - /// The to compare. - /// The to compare. - /// True if the given JsonData represents the given double, and false otherwise. - public static bool operator ==(double left, JsonData? right) - { - if (right is null) - { - return false; - } - - return right.Kind == JsonValueKind.Number && ((double)right) == left; - } - - /// - /// Returns false if a has the same value as a given double, - /// and true otherwise. - /// - /// The to compare. - /// The to compare. - /// False if the given JsonData represents the given double, and false otherwise - public static bool operator !=(double left, JsonData? right) => !(left == right); - } -} diff --git a/sdk/core/Azure.Core.Experimental/src/JsonData.cs b/sdk/core/Azure.Core.Experimental/src/JsonData.cs deleted file mode 100644 index 58b283f9ef0cb..0000000000000 --- a/sdk/core/Azure.Core.Experimental/src/JsonData.cs +++ /dev/null @@ -1,753 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Buffers; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Dynamic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Text; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Azure.Core.Dynamic -{ - /// - /// A mutable representation of a JSON value. - /// - [DebuggerDisplay("{DebuggerDisplay,nq}")] - [DebuggerTypeProxy(typeof(JsonDataDebuggerProxy))] - [JsonConverter(typeof(JsonConverter))] - public partial class JsonData : DynamicData, IDynamicMetaObjectProvider, IEquatable - { - private readonly JsonValueKind _kind; - private Dictionary? _objectRepresentation; - private List? _arrayRepresentation; - private object? _value; - - private static readonly JsonSerializerOptions DefaultJsonSerializerOptions = new JsonSerializerOptions(); - - /// - /// Parses a UTF-8 encoded string representing a single JSON value into a . - /// - /// A UTF-8 encoded string representing a JSON value. - /// A representation of the value. - internal static JsonData Parse(BinaryData utf8Json) - { - using var doc = JsonDocument.Parse(utf8Json); - return new JsonData(doc); - } - - /// - /// Parses test representing a single JSON value into a . - /// - /// The JSON string. - /// A representation of the value. - internal static JsonData Parse(string json) - { - using var doc = JsonDocument.Parse(json); - return new JsonData(doc); - } - - /// - /// Creates a new JsonData object which represents the value of the given JsonDocument. - /// - /// The JsonDocument to convert. - /// A JsonDocument can be constructed from a JSON string using . - internal JsonData(JsonDocument jsonDocument) : this((object?)jsonDocument) - { - } - - /// - /// Creates a new JsonData object which represents the given object. - /// - /// The value to convert. - internal JsonData(object? value) : this(value, DefaultJsonSerializerOptions) - { - } - - /// - /// Creates a new JsonData object which represents the given object. - /// - /// The value to convert. - /// Options to control the conversion behavior. - /// The type of the value to convert. - internal JsonData(object? value, JsonSerializerOptions options, Type? type = null) - { - _value = value; - switch (value) - { - case long l: - _kind = JsonValueKind.Number; - _value = new Number(l); - break; - case int i: - _kind = JsonValueKind.Number; - _value = new Number(i); - break; - case double d: - _kind = JsonValueKind.Number; - _value = new Number(d); - break; - case float d: - _kind = JsonValueKind.Number; - _value = new Number(d); - break; - case bool b when b: - _kind = JsonValueKind.True; - break; - case bool b when !b: - _kind = JsonValueKind.False; - break; - case string: - _kind = JsonValueKind.String; - break; - case null: - _kind = JsonValueKind.Null; - break; - case JsonDocument doc: - _kind = doc.RootElement.ValueKind; - InitFromElement(doc.RootElement); - break; - default: - Type inputType = type ?? (value == null ? typeof(object) : value.GetType()); - - // TODO: Profile to determine if this is the best approach to serialize/parse - using (var stream = new MemoryStream()) - { - using (var writer = new Utf8JsonWriter(stream)) - { - JsonSerializer.Serialize(writer, value, inputType, options); - stream.Position = 0; - JsonElement e = JsonDocument.Parse(stream).RootElement; - _kind = e.ValueKind; - InitFromElement(e); - } - } - break; - } - } - - private JsonData(JsonElement element) - { - _kind = element.ValueKind; - InitFromElement(element); - } - - private void InitFromElement(JsonElement element) - { - switch (element.ValueKind) - { - case JsonValueKind.Object: - _objectRepresentation = new Dictionary(); - foreach (var item in element.EnumerateObject()) - { - _objectRepresentation[item.Name] = new JsonData(item.Value); - } - break; - case JsonValueKind.Array: - _arrayRepresentation = new List(); - foreach (var item in element.EnumerateArray()) - { - _arrayRepresentation.Add(new JsonData(item)); - } - break; - case JsonValueKind.String: - _value = element.GetString(); - break; - case JsonValueKind.Number: - _value = new Number(element); - break; - case JsonValueKind.True: - case JsonValueKind.False: - _value = element.GetBoolean(); - break; - case JsonValueKind.Null: - _value = null; - break; - default: - throw new ArgumentOutOfRangeException(nameof(element), "Unsupported element kind"); - } - } - - /// - /// Gets the value of a property from an object, or null if no such property exists. - /// - /// The name of the property to get - /// The value for a given property, or null if no such property exists. - /// If the property is not this method throws . - internal JsonData? Get(string propertyName) - { - if (EnsureObject().TryGetValue(propertyName, out JsonData value)) - { - return value; - } - - return null; - } - - /// - /// Converts the given JSON value into an instance of a given type. - /// - /// The type to convert the value into. - /// A new instance of constructed from the underlying JSON value. - internal T To() => To(DefaultJsonSerializerOptions); - - /// - /// Deserializes the given JSON value into an instance of a given type. - /// - /// The type to deserialize the value into - /// Options to control the conversion behavior. - /// A new instance of constructed from the underlying JSON value. - internal T To(JsonSerializerOptions options) - { - return JsonSerializer.Deserialize(ToJsonString(), options); - } - - /// - /// Returns a stringified version of the JSON for this value. - /// - /// Returns a stringified version of the JSON for this value. - internal string ToJsonString() - { - using var memoryStream = new MemoryStream(); - using (var writer = new Utf8JsonWriter(memoryStream)) - { - WriteTo(writer); - } - return Encoding.UTF8.GetString(memoryStream.ToArray()); - } - - /// - /// The of the value of this instance. - /// - internal JsonValueKind Kind - { - get => _kind; - } - - /// - /// Returns the number of elements in this array. - /// - /// If is not this methods throws . - internal int Length - { - get => EnsureArray().Count; - } - - /// - /// Returns the names of all the properties of this object. - /// - /// If is not this methods throws . - internal IEnumerable Properties - { - get => EnsureObject().Keys; - } - - /// - /// Returns all the elements in this array. - /// - /// If is not this methods throws . - internal IEnumerable Items - { - get => EnsureArray(); - } - - /// - public override string ToString() - { - if (_kind == JsonValueKind.Object || _kind == JsonValueKind.Array) - { - return ToJsonString(); - } - - return (_value ?? "").ToString(); - } - - /// - public override bool Equals(object? obj) - { - if (obj is string) - { - return this == ((string?)obj); - } - - if (obj is JsonData) - { - return Equals((JsonData)obj); - } - - return base.Equals(obj); - } - - /// - public bool Equals(JsonData other) - { - if (_kind != other._kind) - { - return false; - } - - switch (_kind) - { - case JsonValueKind.Null: - case JsonValueKind.Undefined: - return true; - case JsonValueKind.Object: - return _objectRepresentation!.Equals(other._objectRepresentation); - case JsonValueKind.Array: - return _arrayRepresentation!.Equals(other._arrayRepresentation); - default: - return _value!.Equals(other._value); - } - } - - /// - public override int GetHashCode() - { - if (_kind == JsonValueKind.String) - { - return ((string?)_value)!.GetHashCode(); - } - - return base.GetHashCode(); - } - - private string? GetString() => (string?)EnsureValue(); - - private int GetInt32() - { - var value = EnsureNumberValue().AsLong(); - if (value > int.MaxValue || value < int.MinValue) - { - throw new OverflowException(); - } - return (int)value; - } - - private long GetLong() => EnsureNumberValue().AsLong(); - - private float GetFloat() - { - var value = EnsureNumberValue().AsDouble(); - if (value > float.MaxValue || value < float.MinValue) - { - throw new OverflowException(); - } - return (float)value; - } - private double GetDouble() => EnsureNumberValue().AsDouble(); - - private bool GetBoolean() => (bool)EnsureValue()!; - - internal override void WriteTo(Utf8JsonWriter writer) - { - switch (_kind) - { - case JsonValueKind.Null: - case JsonValueKind.String: - writer.WriteStringValue((string?)_value); - break; - case JsonValueKind.Number: - ((Number)_value!).WriteTo(writer); - break; - case JsonValueKind.True: - case JsonValueKind.False: - writer.WriteBooleanValue((bool)_value!); - break; - case JsonValueKind.Object: - writer.WriteStartObject(); - foreach (var property in EnsureObject()) - { - writer.WritePropertyName(property.Key); - property.Value.WriteTo(writer); - } - writer.WriteEndObject(); - break; - case JsonValueKind.Array: - writer.WriteStartArray(); - foreach (var item in EnsureArray()) - { - item.WriteTo(writer); - } - writer.WriteEndArray(); - break; - } - } - - private Dictionary EnsureObject() - { - if (_kind != JsonValueKind.Object) - { - throw new InvalidOperationException($"Expected kind to be object but was {_kind} instead"); - } - - Debug.Assert(_objectRepresentation != null); - return _objectRepresentation!; - } - - private JsonData? GetPropertyValue(string propertyName) - { - if (EnsureObject().TryGetValue(propertyName, out JsonData element)) - { - return element; - } - - return null; - } - - /// - /// Used by the dynamic meta object to fetch properties. We can't use GetPropertyValue because when the underlying - /// value is an array, we want `.Length` to mean "the length of the array" and not "treat the array as an object - /// and get the Length property", and we also want the return type to be "int" and not a JsonData wrapping the int. - /// - /// The name of the property to get the value of. - /// - private object? GetDynamicPropertyValue(string propertyName) - { - if (_kind == JsonValueKind.Array && propertyName == nameof(Length)) - { - return Length; - } - - if (_kind == JsonValueKind.Object) - { - return GetPropertyValue(propertyName); - } - - throw new InvalidOperationException($"Cannot get property on JSON element with kind {_kind}."); - } - - private JsonData? GetViaIndexer(object index) - { - switch (index) - { - case string propertyName: - return GetPropertyValue(propertyName); - case int arrayIndex: - return GetValueAt(arrayIndex);; - } - - throw new InvalidOperationException($"Tried to access indexer with an unsupported index type: {index}"); - } - - private JsonData SetValue(string propertyName, object value) - { - if (!(value is JsonData json)) - { - json = new JsonData(value); - } - - EnsureObject()[propertyName] = json; - return json; - } - - private List EnsureArray() - { - if (_kind != JsonValueKind.Array) - { - throw new InvalidOperationException($"Expected kind to be array but was {_kind} instead"); - } - - Debug.Assert(_arrayRepresentation != null); - return _arrayRepresentation!; - } - - private JsonData SetViaIndexer(object index, object value) - { - switch (index) - { - case string propertyName: - return SetValue(propertyName, value); - case int arrayIndex: - return SetValueAt(arrayIndex, value); - } - - throw new InvalidOperationException($"Tried to access indexer with an unsupported index type: {index}"); - } - - private JsonData GetValueAt(int index) - { - return EnsureArray()[index]; - } - - private JsonData SetValueAt(int index, object value) - { - if (!(value is JsonData json)) - { - json = new JsonData(value); - } - - EnsureArray()[index] = json; - return json; - } - - private object? EnsureValue() - { - if (_kind == JsonValueKind.Object || _kind == JsonValueKind.Array) - { - throw new InvalidOperationException($"Expected kind to be value but was {_kind} instead"); - } - - return _value; - } - - private Number EnsureNumberValue() - { - if (_kind != JsonValueKind.Number) - { - throw new InvalidOperationException($"Expected kind to be number but was {_kind} instead"); - } - - return (Number)EnsureValue()!; - } - - /// - DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) => new MetaObject(parameter, this); - - private IEnumerable GetDynamicEnumerable() - { - if (_kind == JsonValueKind.Array) - { - return EnsureArray(); - } - - return EnsureObject(); - } - - private string DebuggerDisplay => ToJsonString(); - - private struct Number - { - public Number(in JsonElement element) - { - _hasDouble = element.TryGetDouble(out _double); - _hasLong = element.TryGetInt64(out _long); - } - - public Number(long l) - { - _long = l; - _hasLong = true; - _double = default; - _hasDouble = false; - } - - private long _long; - private bool _hasLong; - private double _double; - private bool _hasDouble; - - public Number(double d) - { - _long = default; - _hasLong = false; - _double = d; - _hasDouble = true; - } - - public void WriteTo(Utf8JsonWriter writer) - { - if (_hasDouble) - { - writer.WriteNumberValue(_double); - } - else - { - writer.WriteNumberValue(_long); - } - } - - public long AsLong() - { - if (!_hasLong) - { - throw new FormatException(); - } - return _long; - } - - public double AsDouble() - { - return _double; - } - } - - private class MetaObject : DynamicMetaObject - { - private static readonly MethodInfo GetDynamicValueMethod = typeof(JsonData).GetMethod(nameof(GetDynamicPropertyValue), BindingFlags.NonPublic | BindingFlags.Instance); - - private static readonly MethodInfo GetDynamicEnumerableMethod = typeof(JsonData).GetMethod(nameof(GetDynamicEnumerable), BindingFlags.NonPublic | BindingFlags.Instance); - - private static readonly MethodInfo SetValueMethod = typeof(JsonData).GetMethod(nameof(SetValue), BindingFlags.NonPublic | BindingFlags.Instance); - - private static readonly MethodInfo GetViaIndexerMethod = typeof(JsonData).GetMethod(nameof(GetViaIndexer), BindingFlags.NonPublic | BindingFlags.Instance); - - private static readonly MethodInfo SetViaIndexerMethod = typeof(JsonData).GetMethod(nameof(SetViaIndexer), BindingFlags.NonPublic | BindingFlags.Instance); - - // Operators that cast from JsonData to another type - private static readonly Dictionary CastFromOperators = GetCastFromOperators(); - - internal MetaObject(Expression parameter, IDynamicMetaObjectProvider value) : base(parameter, BindingRestrictions.Empty, value) - { - } - - public override DynamicMetaObject BindGetMember(GetMemberBinder binder) - { - var targetObject = Expression.Convert(Expression, LimitType); - - var arguments = new Expression[] { Expression.Constant(binder.Name) }; - var getPropertyCall = Expression.Call(targetObject, GetDynamicValueMethod, arguments); - - var restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType); - return new DynamicMetaObject(getPropertyCall, restrictions); - } - - public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes) - { - var targetObject = Expression.Convert(Expression, LimitType); - var arguments = new Expression[] { Expression.Convert(indexes[0].Expression, typeof(object)) }; - var getViaIndexerCall = Expression.Call(targetObject, GetViaIndexerMethod, arguments); - - var restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType); - return new DynamicMetaObject(getViaIndexerCall, restrictions); - } - - public override DynamicMetaObject BindConvert(ConvertBinder binder) - { - Expression targetObject = Expression.Convert(Expression, LimitType); - BindingRestrictions restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType); - - Expression convertCall; - - if (binder.Type == typeof(IEnumerable)) - { - convertCall = Expression.Call(targetObject, GetDynamicEnumerableMethod); - return new DynamicMetaObject(convertCall, restrictions); - } - - if (CastFromOperators.TryGetValue(binder.Type, out MethodInfo? castOperator)) - { - convertCall = Expression.Call(castOperator, targetObject); - return new DynamicMetaObject(convertCall, restrictions); - } - - convertCall = Expression.Call(targetObject, nameof(To), new Type[] { binder.Type }); - return new DynamicMetaObject(convertCall, restrictions); - } - - public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) - { - Expression targetObject = Expression.Convert(Expression, LimitType); - var arguments = new Expression[2] { Expression.Constant(binder.Name), Expression.Convert(value.Expression, typeof(object)) }; - - Expression setPropertyCall = Expression.Call(targetObject, SetValueMethod, arguments); - BindingRestrictions restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType); - DynamicMetaObject setProperty = new DynamicMetaObject(setPropertyCall, restrictions); - return setProperty; - } - - public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value) - { - var targetObject = Expression.Convert(Expression, LimitType); - var arguments = new Expression[2] { - Expression.Convert(indexes[0].Expression, typeof(object)), - Expression.Convert(value.Expression, typeof(object)) - }; - var setCall = Expression.Call(targetObject, SetViaIndexerMethod, arguments); - - var restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType); - return new DynamicMetaObject(setCall, restrictions); - } - - private static Dictionary GetCastFromOperators() - { - return typeof(JsonData) - .GetMethods(BindingFlags.Public | BindingFlags.Static) - .Where(method => method.Name == "op_Explicit" || method.Name == "op_Implicit") - .ToDictionary(method => method.ReturnType); - } - } - - internal class JsonDataDebuggerProxy - { - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly JsonData _jsonData; - - public JsonDataDebuggerProxy(JsonData jsonData) - { - _jsonData = jsonData; - } - - [DebuggerDisplay("{Value.DebuggerDisplay,nq}", Name = "{Name,nq}")] - internal class PropertyMember - { - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public string? Name { get; set; } - [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public JsonData? Value { get; set; } - } - - [DebuggerDisplay("{Value,nq}")] - internal class SingleMember - { - public object? Value { get; set; } - } - - [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public object Members - { - get - { - if (_jsonData.Kind != JsonValueKind.Array && - _jsonData.Kind != JsonValueKind.Object) - return new SingleMember() { Value = _jsonData.ToJsonString() }; - - return BuildMembers().ToArray(); - } - } - - private IEnumerable BuildMembers() - { - if (_jsonData.Kind == JsonValueKind.Object) - { - foreach (var property in _jsonData.Properties) - { - yield return new PropertyMember() { Name = property, Value = _jsonData.Get(property) }; - } - } - else if (_jsonData.Kind == JsonValueKind.Array) - { - foreach (var property in _jsonData.Items) - { - yield return property; - } - } - } - } - - /// - /// The default serialization behavior for is not the behavior we want, we want to use - /// the underlying JSON value that wraps, instead of using the default behavior for - /// POCOs. - /// - private class JsonConverter : JsonConverter - { - public override JsonData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - using var document = JsonDocument.ParseValue(ref reader); - return new JsonData(document); - } - - public override void Write(Utf8JsonWriter writer, JsonData value, JsonSerializerOptions options) - { - value.WriteTo(writer); - } - } - } -} diff --git a/sdk/core/Azure.Core.Experimental/src/Properties/AssemblyInfo.cs b/sdk/core/Azure.Core.Experimental/src/Properties/AssemblyInfo.cs index a0f33f09c81f9..6fad36ddded16 100644 --- a/sdk/core/Azure.Core.Experimental/src/Properties/AssemblyInfo.cs +++ b/sdk/core/Azure.Core.Experimental/src/Properties/AssemblyInfo.cs @@ -5,5 +5,6 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Azure.Core.Experimental.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d15ddcb29688295338af4b7686603fe614abd555e09efba8fb88ee09e1f7b1ccaeed2e8f823fa9eef3fdd60217fc012ea67d2479751a0b8c087a4185541b851bd8b16f8d91b840e51b1cb0ba6fe647997e57429265e85ef62d565db50a69ae1647d54d7bd855e4db3d8a91510e5bcbd0edfbbecaa20a7bd9ae74593daa7b11b4")] +[assembly: InternalsVisibleTo("Azure.Core.Experimental.Perf.Benchmarks, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d15ddcb29688295338af4b7686603fe614abd555e09efba8fb88ee09e1f7b1ccaeed2e8f823fa9eef3fdd60217fc012ea67d2479751a0b8c087a4185541b851bd8b16f8d91b840e51b1cb0ba6fe647997e57429265e85ef62d565db50a69ae1647d54d7bd855e4db3d8a91510e5bcbd0edfbbecaa20a7bd9ae74593daa7b11b4")] [assembly: SuppressMessage("Usage", "AZC0014:Avoid using banned types in public API", Justification = "Azure.Core.Experimental ships adapters for System.Text.Json", Scope = "type", Target = "~T:Azure.Core.Dynamic.DynamicData")] diff --git a/sdk/core/Azure.Core.Experimental/tests/JsonDataDynamicMutableTests.cs b/sdk/core/Azure.Core.Experimental/tests/JsonDataDynamicMutableTests.cs index eb977062bc874..932e1e2fd5dc9 100644 --- a/sdk/core/Azure.Core.Experimental/tests/JsonDataDynamicMutableTests.cs +++ b/sdk/core/Azure.Core.Experimental/tests/JsonDataDynamicMutableTests.cs @@ -6,46 +6,46 @@ using Azure.Core.GeoJson; using NUnit.Framework; -namespace Azure.Core.Tests +namespace Azure.Core.Experimental.Tests { public class JsonDataDynamicMutableTests { [Test] public void ArrayItemsCanBeAssigned() { - var json = JsonData.Parse("[0, 1, 2, 3]"); - dynamic jsonData = json; - jsonData[1] = 2; - jsonData[2] = null; - jsonData[3] = "string"; + dynamic json = DynamicJsonTests.GetDynamicJson("[0, 1, 2, 3]"); - Assert.AreEqual(jsonData.ToString(), "[0,2,null,\"string\"]"); + json[1] = 2; + json[2] = null; + json[3] = "string"; + + Assert.AreEqual("[0,2,null,\"string\"]", json.ToString()); } [Test] public void ExistingObjectPropertiesCanBeAssigned() { - var json = JsonData.Parse("{\"a\":1}"); - dynamic jsonData = json; - jsonData.a = "2"; + dynamic json = DynamicJsonTests.GetDynamicJson("{\"a\":1}"); + + json.a = "2"; - Assert.AreEqual(json.ToString(), "{\"a\":\"2\"}"); + Assert.AreEqual("{\"a\":\"2\"}", json.ToString()); } [TestCaseSource(nameof(PrimitiveValues))] public void NewObjectPropertiesCanBeAssignedWithPrimitive(T value, string expected) { - var json = JsonData.Parse("{}"); - dynamic jsonData = json; - jsonData.a = value; + dynamic json = DynamicJsonTests.GetDynamicJson("{}"); - Assert.AreEqual(json.ToString(), "{\"a\":" + expected + "}"); + json.a = value; + + Assert.AreEqual("{\"a\":" + expected + "}", json.ToString()); } [TestCaseSource(nameof(PrimitiveValues))] public void PrimitiveValuesCanBeParsedDirectly(T value, string expected) { - dynamic json = JsonData.Parse(expected); + dynamic json = DynamicJsonTests.GetDynamicJson(expected); Assert.AreEqual(value, (T)json); } @@ -53,42 +53,42 @@ public void PrimitiveValuesCanBeParsedDirectly(T value, string expected) [Test] public void NewObjectPropertiesCanBeAssignedWithArrays() { - var json = JsonData.Parse("{}"); - dynamic jsonData = json; - jsonData.a = new JsonData(new object[] { 1, 2, null, "string" }); + dynamic json = DynamicJsonTests.GetDynamicJson("{}"); + + json.a = new MutableJsonDocument(new object[] { 1, 2, null, "string" }); - Assert.AreEqual(json.ToString(), "{\"a\":[1,2,null,\"string\"]}"); + Assert.AreEqual("{\"a\":[1,2,null,\"string\"]}", json.ToString()); } [Test] public void NewObjectPropertiesCanBeAssignedWithObject() { - var json = JsonData.Parse("{}"); - dynamic jsonData = json; - jsonData.a = JsonData.Parse("{}"); - jsonData.a.b = 2; + dynamic json = DynamicJsonTests.GetDynamicJson("{}"); - Assert.AreEqual(json.ToString(), "{\"a\":{\"b\":2}}"); + json.a = DynamicJsonTests.GetDynamicJson("{}"); + json.a.b = 2; + + Assert.AreEqual("{\"a\":{\"b\":2}}", json.ToString()); } [Test] public void NewObjectPropertiesCanBeAssignedWithObjectIndirectly() { - var json = JsonData.Parse("{}"); - dynamic jsonData = json; - dynamic anotherJson = JsonData.Parse("{}"); - jsonData.a = anotherJson; + dynamic json = DynamicJsonTests.GetDynamicJson("{}"); + dynamic anotherJson = DynamicJsonTests.GetDynamicJson("{}"); + + json.a = anotherJson; anotherJson.b = 2; - Assert.AreEqual(json.ToString(), "{\"a\":{\"b\":2}}"); + Assert.AreEqual("{\"a\":{\"b\":2}}", json.ToString()); } [Test] public void NewObjectPropertiesCanBeAssignedWithSerializedObject() { - var json = JsonData.Parse("{}"); - dynamic jsonData = json; - jsonData.a = new JsonData(new GeoPoint(1, 2)); + dynamic json = DynamicJsonTests.GetDynamicJson("{}"); + + json.a = new MutableJsonDocument(new GeoPoint(1, 2)); Assert.AreEqual("{\"a\":{\"type\":\"Point\",\"coordinates\":[1,2]}}", json.ToString()); } @@ -96,14 +96,14 @@ public void NewObjectPropertiesCanBeAssignedWithSerializedObject() [TestCaseSource(nameof(PrimitiveValues))] public void CanModifyNestedProperties(T value, string expected) { - var json = JsonData.Parse("{\"a\":{\"b\":2}}"); - dynamic jsonData = json; - jsonData.a.b = value; + dynamic json = DynamicJsonTests.GetDynamicJson("{\"a\":{\"b\":2}}"); + + json.a.b = value; - Assert.AreEqual(json.ToString(), "{\"a\":{\"b\":" + expected + "}}"); - Assert.AreEqual(value, (T)jsonData.a.b); + Assert.AreEqual("{\"a\":{\"b\":" + expected + "}}", json.ToString()); + Assert.AreEqual(value, (T)json.a.b); - dynamic reparsedJson = JsonData.Parse(json.ToString()); + dynamic reparsedJson = DynamicJsonTests.GetDynamicJson(json.ToString()); Assert.AreEqual(value, (T)reparsedJson.a.b); } @@ -116,10 +116,10 @@ public static IEnumerable PrimitiveValues() yield return new object[] {1.0, "1"}; #if NETCOREAPP yield return new object[] {1.1D, "1.1"}; - yield return new object[] {1.1F, "1.100000023841858"}; + yield return new object[] {1.1F, "1.1"}; #else yield return new object[] {1.1D, "1.1000000000000001"}; - yield return new object[] {1.1F, "1.1000000238418579"}; + yield return new object[] {1.1F, "1.10000002" }; #endif yield return new object[] {true, "true"}; yield return new object[] {false, "false"}; diff --git a/sdk/core/Azure.Core.Experimental/tests/JsonDataTests.cs b/sdk/core/Azure.Core.Experimental/tests/JsonDataTests.cs index de0dd47650c33..31539583b8797 100644 --- a/sdk/core/Azure.Core.Experimental/tests/JsonDataTests.cs +++ b/sdk/core/Azure.Core.Experimental/tests/JsonDataTests.cs @@ -2,29 +2,28 @@ // Licensed under the MIT License. using System; -using System.Linq; using System.Text.Json; using Azure.Core.Dynamic; using NUnit.Framework; -namespace Azure.Core.Tests +namespace Azure.Core.Experimental.Tests { public class JsonDataTests { - [Test] - public void CanCreateFromJson() - { - var jsonData = JsonData.Parse("\"string\""); + //[Test] + //public void CanCreateFromJson() + //{ + // var jsonData = DynamicJsonTests.GetDynamicJson("\"string\""); - Assert.AreEqual("\"string\"", jsonData.ToJsonString()); - } + // Assert.AreEqual("\"string\"", jsonData.ToJsonString()); + //} - [Test] - public void CanCreateFromNull() - { - var jsonData = new JsonData(null); - Assert.AreEqual(JsonValueKind.Null, jsonData.Kind); - } + //[Test] + //public void CanCreateFromNull() + //{ + // var jsonData = new JsonData(null); + // Assert.AreEqual(JsonValueKind.Null, jsonData.Kind); + //} [Test] public void DynamicCanConvertToString() => Assert.AreEqual("string", JsonAsType("\"string\"")); @@ -53,7 +52,7 @@ public void CanCreateFromNull() [Test] public void DynamicCanConvertToIEnumerableDynamic() { - dynamic jsonData = JsonData.Parse("[1, null, \"s\"]"); + dynamic jsonData = DynamicJsonTests.GetDynamicJson("[1, null, \"s\"]"); int i = 0; foreach (var dynamicItem in jsonData) { @@ -81,7 +80,7 @@ public void DynamicCanConvertToIEnumerableDynamic() [Test] public void DynamicCanConvertToIEnumerableInt() { - dynamic jsonData = JsonData.Parse("[0, 1, 2, 3]"); + dynamic jsonData = DynamicJsonTests.GetDynamicJson("[0, 1, 2, 3]"); int i = 0; foreach (int dynamicItem in jsonData) { @@ -92,209 +91,198 @@ public void DynamicCanConvertToIEnumerableInt() Assert.AreEqual(4, i); } - [Test] - public void DynamicArrayHasLength() - { - dynamic jsonData = JsonData.Parse("[0, 1, 2, 3]"); - Assert.AreEqual(4, jsonData.Length); - } - - [Test] - public void DynamicArrayFor() - { - dynamic jsonData = JsonData.Parse("[0, 1, 2, 3]"); - for (int i = 0; i < jsonData.Length; i++) - { - Assert.AreEqual(i, (int)jsonData[i]); - } - } - [Test] public void CanAccessProperties() { - dynamic jsonData = JsonData.Parse("{ \"primitive\":\"Hello\", \"nested\": { \"nestedPrimitive\":true } }"); + dynamic jsonData = DynamicJsonTests.GetDynamicJson(@" + { + ""primitive"" : ""Hello"", + ""nested"" : { + ""nestedPrimitive"": true + } + }"); Assert.AreEqual("Hello", (string)jsonData.primitive); Assert.AreEqual(true, (bool)jsonData.nested.nestedPrimitive); } - [Test] - public void CanReadIntsAsFloatingPoints() - { - var json = JsonData.Parse("5"); - dynamic jsonData = json; - - Assert.AreEqual(5, (float)jsonData); - Assert.AreEqual(5, (double)jsonData); - Assert.AreEqual(5, (int)jsonData); - Assert.AreEqual(5, (long)jsonData); - Assert.AreEqual(5, (float)json); - Assert.AreEqual(5, (double)json); - Assert.AreEqual(5, (int)json); - Assert.AreEqual(5, (long)json); - } - - [Test] - public void ReadingFloatingPointAsIntThrows() - { - var json = JsonData.Parse("5.5"); - dynamic jsonData = json; - Assert.Throws(() => _ = (int)json); - Assert.Throws(() => _ = (int)jsonData); - Assert.Throws(() => _ = (long)json); - Assert.Throws(() => _ = (long)jsonData); - } - - [Test] - public void FloatOverflowThrows() - { - var json = JsonData.Parse("34028234663852885981170418348451692544000"); - dynamic jsonData = json; - Assert.Throws(() => _ = (float)json); - Assert.Throws(() => _ = (float)jsonData); - Assert.AreEqual(34028234663852885981170418348451692544000d, (double)jsonData); - Assert.AreEqual(34028234663852885981170418348451692544000d, (double)json); - } - - [Test] - public void FloatUnderflowThrows() - { - var json = JsonData.Parse("-34028234663852885981170418348451692544000"); - dynamic jsonData = json; - Assert.Throws(() => _ = (float)json); - Assert.Throws(() => _ = (float)jsonData); - Assert.AreEqual(-34028234663852885981170418348451692544000d, (double)jsonData); - Assert.AreEqual(-34028234663852885981170418348451692544000d, (double)json); - } - - [Test] - public void IntOverflowThrows() - { - var json = JsonData.Parse("3402823466385288598"); - dynamic jsonData = json; - Assert.Throws(() => _ = (int)json); - Assert.Throws(() => _ = (int)jsonData); - Assert.AreEqual(3402823466385288598L, (long)jsonData); - Assert.AreEqual(3402823466385288598L, (long)json); - Assert.AreEqual(3402823466385288598D, (double)jsonData); - Assert.AreEqual(3402823466385288598D, (double)json); - Assert.AreEqual(3402823466385288598F, (float)jsonData); - Assert.AreEqual(3402823466385288598F, (float)json); - } - - [Test] - public void IntUnderflowThrows() - { - var json = JsonData.Parse("-3402823466385288598"); - dynamic jsonData = json; - Assert.Throws(() => _ = (int)json); - Assert.Throws(() => _ = (int)jsonData); - Assert.AreEqual(-3402823466385288598L, (long)jsonData); - Assert.AreEqual(-3402823466385288598L, (long)json); - Assert.AreEqual(-3402823466385288598D, (double)jsonData); - Assert.AreEqual(-3402823466385288598D, (double)json); - Assert.AreEqual(-3402823466385288598F, (float)jsonData); - Assert.AreEqual(-3402823466385288598F, (float)json); - } - - [Test] - public void ReadingArrayAsValueThrows() - { - var json = JsonData.Parse("[1,3]"); - dynamic jsonData = json; - Assert.Throws(() => _ = (int)json); - Assert.Throws(() => _ = (int)jsonData); - } - - [Test] - public void RoundtripObjects() - { - var model = new SampleModel("Hello World", 5); - var roundtripped = new JsonData(model).To(); - - Assert.AreEqual(model, roundtripped); - } - - [Test] - public void EqualsProvidesValueEqualityPrimitives() - { - Assert.AreEqual(new JsonData(1), new JsonData(1)); - Assert.AreEqual(new JsonData(true), new JsonData(true)); - Assert.AreEqual(new JsonData(false), new JsonData(false)); - Assert.AreEqual(new JsonData("hello"), new JsonData("hello")); - Assert.AreEqual(new JsonData(null), new JsonData(null)); - } - - [Test] - public void EqualsHandlesStringsSpecial() - { - Assert.IsTrue((new JsonData("test").Equals("test"))); - Assert.IsTrue((new JsonData("test").Equals(new JsonData("test")))); - } - - [Test] - public void EqualsForObjectsAndArrays() - { - JsonData obj1 = new JsonData(new { foo = "bar" }); - JsonData obj2 = new JsonData(new { foo = "bar" }); - - JsonData arr1 = new JsonData(new[] { "bar" }); - JsonData arr2 = new JsonData(new[] { "bar" }); - - // For objects and arrays, Equals provides reference equality. - Assert.AreEqual(obj1, obj1); - Assert.AreEqual(arr1, arr1); - - Assert.AreNotEqual(obj1, obj2); - Assert.AreNotEqual(arr1, arr2); - } - - [Test] - public void EqualsAndNull() - { - Assert.AreNotEqual(new JsonData(null), null); - Assert.AreNotEqual(null, new JsonData(null)); - } - - [Test] - public void OperatorEqualsForString() - { - Assert.IsTrue(new JsonData("foo") == "foo"); - Assert.IsTrue("foo" == new JsonData("foo")); - Assert.IsFalse(new JsonData("foo") != "foo"); - Assert.IsFalse("foo" != new JsonData("foo")); - - Assert.IsFalse(new JsonData("bar") == "foo"); - Assert.IsFalse("foo" == new JsonData("bar")); - Assert.IsTrue(new JsonData("bar") != "foo"); - Assert.IsTrue("foo" != new JsonData("bar")); - } - - [Test] - public void JsonDataInPOCOsWorks() - { - JsonData orig = new JsonData(new - { - property = new JsonData("hello") - }); - - void validate(JsonData d) - { - Assert.AreEqual(JsonValueKind.Object, d.Kind); - Assert.AreEqual(d.Properties.Count(), 1); - Assert.AreEqual(d.Get("property"), "hello"); - } - - validate(orig); - - JsonData roundTrip = JsonSerializer.Deserialize(JsonSerializer.Serialize(orig, orig.GetType())); - - validate(roundTrip); - } + //[Test] + //public void CanReadIntsAsFloatingPoints() + //{ + // var json = JsonData.Parse("5"); + // dynamic jsonData = json; + + // Assert.AreEqual(5, (float)jsonData); + // Assert.AreEqual(5, (double)jsonData); + // Assert.AreEqual(5, (int)jsonData); + // Assert.AreEqual(5, (long)jsonData); + // Assert.AreEqual(5, (float)json); + // Assert.AreEqual(5, (double)json); + // Assert.AreEqual(5, (int)json); + // Assert.AreEqual(5, (long)json); + //} + + //[Test] + //public void ReadingFloatingPointAsIntThrows() + //{ + // var json = JsonData.Parse("5.5"); + // dynamic jsonData = json; + // Assert.Throws(() => _ = (int)json); + // Assert.Throws(() => _ = (int)jsonData); + // Assert.Throws(() => _ = (long)json); + // Assert.Throws(() => _ = (long)jsonData); + //} + + //[Test] + //public void FloatOverflowThrows() + //{ + // var json = JsonData.Parse("34028234663852885981170418348451692544000"); + // dynamic jsonData = json; + // Assert.Throws(() => _ = (float)json); + // Assert.Throws(() => _ = (float)jsonData); + // Assert.AreEqual(34028234663852885981170418348451692544000d, (double)jsonData); + // Assert.AreEqual(34028234663852885981170418348451692544000d, (double)json); + //} + + //[Test] + //public void FloatUnderflowThrows() + //{ + // var json = JsonData.Parse("-34028234663852885981170418348451692544000"); + // dynamic jsonData = json; + // Assert.Throws(() => _ = (float)json); + // Assert.Throws(() => _ = (float)jsonData); + // Assert.AreEqual(-34028234663852885981170418348451692544000d, (double)jsonData); + // Assert.AreEqual(-34028234663852885981170418348451692544000d, (double)json); + //} + + //[Test] + //public void IntOverflowThrows() + //{ + // var json = JsonData.Parse("3402823466385288598"); + // dynamic jsonData = json; + // Assert.Throws(() => _ = (int)json); + // Assert.Throws(() => _ = (int)jsonData); + // Assert.AreEqual(3402823466385288598L, (long)jsonData); + // Assert.AreEqual(3402823466385288598L, (long)json); + // Assert.AreEqual(3402823466385288598D, (double)jsonData); + // Assert.AreEqual(3402823466385288598D, (double)json); + // Assert.AreEqual(3402823466385288598F, (float)jsonData); + // Assert.AreEqual(3402823466385288598F, (float)json); + //} + + //[Test] + //public void IntUnderflowThrows() + //{ + // var json = JsonData.Parse("-3402823466385288598"); + // dynamic jsonData = json; + // Assert.Throws(() => _ = (int)json); + // Assert.Throws(() => _ = (int)jsonData); + // Assert.AreEqual(-3402823466385288598L, (long)jsonData); + // Assert.AreEqual(-3402823466385288598L, (long)json); + // Assert.AreEqual(-3402823466385288598D, (double)jsonData); + // Assert.AreEqual(-3402823466385288598D, (double)json); + // Assert.AreEqual(-3402823466385288598F, (float)jsonData); + // Assert.AreEqual(-3402823466385288598F, (float)json); + //} + + //[Test] + //public void ReadingArrayAsValueThrows() + //{ + // var json = JsonData.Parse("[1,3]"); + // dynamic jsonData = json; + // Assert.Throws(() => _ = (int)json); + // Assert.Throws(() => _ = (int)jsonData); + //} + + //[Test] + //public void RoundtripObjects() + //{ + // var model = new SampleModel("Hello World", 5); + // var roundtripped = new JsonData(model).To(); + + // Assert.AreEqual(model, roundtripped); + //} + + //[Test] + //public void EqualsProvidesValueEqualityPrimitives() + //{ + // Assert.AreEqual(new JsonData(1), new JsonData(1)); + // Assert.AreEqual(new JsonData(true), new JsonData(true)); + // Assert.AreEqual(new JsonData(false), new JsonData(false)); + // Assert.AreEqual(new JsonData("hello"), new JsonData("hello")); + // Assert.AreEqual(new JsonData(null), new JsonData(null)); + //} + + //[Test] + //public void EqualsHandlesStringsSpecial() + //{ + // Assert.IsTrue((new JsonData("test").Equals("test"))); + // Assert.IsTrue((new JsonData("test").Equals(new JsonData("test")))); + //} + + //[Test] + //public void EqualsForObjectsAndArrays() + //{ + // JsonData obj1 = new JsonData(new { foo = "bar" }); + // JsonData obj2 = new JsonData(new { foo = "bar" }); + + // JsonData arr1 = new JsonData(new[] { "bar" }); + // JsonData arr2 = new JsonData(new[] { "bar" }); + + // // For objects and arrays, Equals provides reference equality. + // Assert.AreEqual(obj1, obj1); + // Assert.AreEqual(arr1, arr1); + + // Assert.AreNotEqual(obj1, obj2); + // Assert.AreNotEqual(arr1, arr2); + //} + + //[Test] + //public void EqualsAndNull() + //{ + // Assert.AreNotEqual(new JsonData(null), null); + // Assert.AreNotEqual(null, new JsonData(null)); + //} + + //[Test] + //public void OperatorEqualsForString() + //{ + // Assert.IsTrue(new JsonData("foo") == "foo"); + // Assert.IsTrue("foo" == new JsonData("foo")); + // Assert.IsFalse(new JsonData("foo") != "foo"); + // Assert.IsFalse("foo" != new JsonData("foo")); + + // Assert.IsFalse(new JsonData("bar") == "foo"); + // Assert.IsFalse("foo" == new JsonData("bar")); + // Assert.IsTrue(new JsonData("bar") != "foo"); + // Assert.IsTrue("foo" != new JsonData("bar")); + //} + + ////[Test] + ////public void JsonDataInPOCOsWorks() + ////{ + //// JsonData orig = new JsonData(new + //// { + //// property = new JsonData("hello") + //// }); + + //// void validate(JsonData d) + //// { + //// Assert.AreEqual(JsonValueKind.Object, d.Kind); + //// Assert.AreEqual(d.Properties.Count(), 1); + //// Assert.AreEqual(d.Get("property"), "hello"); + //// } + + //// validate(orig); + + //// JsonData roundTrip = JsonSerializer.Deserialize(JsonSerializer.Serialize(orig, orig.GetType())); + + //// validate(roundTrip); + ////} private T JsonAsType(string json) { - dynamic jsonData = JsonData.Parse(json); + dynamic jsonData = DynamicJsonTests.GetDynamicJson(json); return (T) jsonData; } diff --git a/sdk/core/Azure.Core.Experimental/tests/perf/DynamicReadingBenchmark.cs b/sdk/core/Azure.Core.Experimental/tests/perf/DynamicReadingBenchmark.cs index 9576d4513ad52..608d2bb1bd03e 100644 --- a/sdk/core/Azure.Core.Experimental/tests/perf/DynamicReadingBenchmark.cs +++ b/sdk/core/Azure.Core.Experimental/tests/perf/DynamicReadingBenchmark.cs @@ -7,7 +7,7 @@ using BenchmarkDotNet.Attributes; using Newtonsoft.Json.Linq; -namespace Azure.Data.AppConfiguration.Performance +namespace Azure.Core.Experimental.Perf.Benchmarks { [MemoryDiagnoser] [InProcess] diff --git a/sdk/core/Azure.Core.Experimental/tests/perf/FirstReadBenchmark.cs b/sdk/core/Azure.Core.Experimental/tests/perf/FirstReadBenchmark.cs index 586af969e4502..c31c4c26ba10f 100644 --- a/sdk/core/Azure.Core.Experimental/tests/perf/FirstReadBenchmark.cs +++ b/sdk/core/Azure.Core.Experimental/tests/perf/FirstReadBenchmark.cs @@ -6,7 +6,7 @@ using Azure.Core.Dynamic; using BenchmarkDotNet.Attributes; -namespace Azure.Data.AppConfiguration.Performance +namespace Azure.Core.Experimental.Perf.Benchmarks { [MemoryDiagnoser] public class FirstReadBenchmark diff --git a/sdk/core/Azure.Core.Experimental/tests/perf/Program.cs b/sdk/core/Azure.Core.Experimental/tests/perf/Program.cs index 22abac135b7b1..4fd26cc86ea99 100644 --- a/sdk/core/Azure.Core.Experimental/tests/perf/Program.cs +++ b/sdk/core/Azure.Core.Experimental/tests/perf/Program.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; +using Azure.Core.Experimental.Perf.Benchmarks; using BenchmarkDotNet.Running; namespace Azure.Core.Experimental.Performance @@ -10,6 +12,17 @@ public class Program public static void Main(string[] args) { BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); + + //DebugBenchmark(); + } + + private static void DebugBenchmark() + { + ReadLargePayloadBenchmark b = new(); + + string value = b.ReadJsonElement(); + + value = b.ReadJsonData(); } } } diff --git a/sdk/core/Azure.Core.Experimental/tests/public/JsonDataArrayTests.cs b/sdk/core/Azure.Core.Experimental/tests/public/JsonDataArrayTests.cs index e789fbdc8f572..33257ed355fa5 100644 --- a/sdk/core/Azure.Core.Experimental/tests/public/JsonDataArrayTests.cs +++ b/sdk/core/Azure.Core.Experimental/tests/public/JsonDataArrayTests.cs @@ -241,28 +241,14 @@ public void CanSetObjectMemberViaArrayIndex() public void CanGetArrayLength() { dynamic data = JsonDataTestHelpers.CreateFromJson("[1, 2, 3]"); - Assert.AreEqual(3, data.Length); + Assert.AreEqual(3, ((int[])data).Length); } [Test] public void CanGetArrayPropertyLength() { dynamic data = JsonDataTestHelpers.CreateFromJson(@"{ ""value"": [1, 2, 3] }"); - Assert.AreEqual(3, data.value.Length); - } - - [Test] - public void CannotSetArrayLength() - { - dynamic data = JsonDataTestHelpers.CreateFromJson("[1, 2, 3]"); - Assert.Throws(() => { data.Length = 5; }); - } - - [Test] - public void CannotSetArrayPropertyLength() - { - dynamic data = JsonDataTestHelpers.CreateFromJson(@"{ ""value"": [1, 2, 3] }"); - Assert.Throws(() => { data.value.Length = 5; }); + Assert.AreEqual(3, ((int[])data.value).Length); } [Test] diff --git a/sdk/core/Azure.Core.Experimental/tests/public/JsonDataLeafTests.cs b/sdk/core/Azure.Core.Experimental/tests/public/JsonDataLeafTests.cs index 5be4a0bf5b3eb..94c0076d691eb 100644 --- a/sdk/core/Azure.Core.Experimental/tests/public/JsonDataLeafTests.cs +++ b/sdk/core/Azure.Core.Experimental/tests/public/JsonDataLeafTests.cs @@ -57,7 +57,7 @@ public void CanConvertIntLeafPropertyToLong() public void CannotConvertIntLeafToString() { dynamic data = JsonDataTestHelpers.CreateFromJson("5"); - Assert.Throws( + Assert.Throws( () => { var s = (string)data; } ); } @@ -66,7 +66,7 @@ public void CannotConvertIntLeafToString() public void CannotConvertIntLeafPropertyToString() { dynamic data = JsonDataTestHelpers.CreateFromJson(@"{ ""value"": 5 }"); - Assert.Throws( + Assert.Throws( () => { var s = (string)data.value; } ); } @@ -75,7 +75,7 @@ public void CannotConvertIntLeafPropertyToString() public void CannotConvertIntLeafToBoolean() { dynamic data = JsonDataTestHelpers.CreateFromJson("5"); - Assert.Throws( + Assert.Throws( () => { var b = (bool)data; } ); } @@ -84,7 +84,7 @@ public void CannotConvertIntLeafToBoolean() public void CannotConvertIntLeafPropertyToBoolean() { dynamic data = JsonDataTestHelpers.CreateFromJson(@"{ ""value"": 5 }"); - Assert.Throws( + Assert.Throws( () => { var b = (bool)data.value; } ); } diff --git a/sdk/core/Azure.Core.Experimental/tests/public/JsonDataObjectTests.cs b/sdk/core/Azure.Core.Experimental/tests/public/JsonDataObjectTests.cs index 1150ee6318056..c2a1d90afe765 100644 --- a/sdk/core/Azure.Core.Experimental/tests/public/JsonDataObjectTests.cs +++ b/sdk/core/Azure.Core.Experimental/tests/public/JsonDataObjectTests.cs @@ -115,6 +115,7 @@ public void CannotSetArrayIndexOnObject() #endregion [Test] + [Ignore("To be implemented.")] public void CanEnumerateObject() { dynamic data = JsonDataTestHelpers.CreateFromJson(@"{ ""first"": 1, ""second"": 2 }"); diff --git a/sdk/core/Azure.Core.Experimental/tests/public/JsonDataPublicMutableTests.cs b/sdk/core/Azure.Core.Experimental/tests/public/JsonDataPublicMutableTests.cs index 7400251ff6dae..bbce70b13b274 100644 --- a/sdk/core/Azure.Core.Experimental/tests/public/JsonDataPublicMutableTests.cs +++ b/sdk/core/Azure.Core.Experimental/tests/public/JsonDataPublicMutableTests.cs @@ -108,10 +108,10 @@ public static IEnumerable PrimitiveValues() yield return new object[] { 1.0, "1" }; #if NETCOREAPP yield return new object[] {1.1D, "1.1"}; - yield return new object[] {1.1F, "1.100000023841858"}; + yield return new object[] {1.1F, "1.1"}; #else yield return new object[] { 1.1D, "1.1000000000000001" }; - yield return new object[] { 1.1F, "1.1000000238418579" }; + yield return new object[] { 1.1F, "1.10000002" }; #endif yield return new object[] { true, "true" }; yield return new object[] { false, "false" }; diff --git a/sdk/core/Azure.Core.Experimental/tests/public/JsonDataPublicTests.cs b/sdk/core/Azure.Core.Experimental/tests/public/JsonDataPublicTests.cs index 37a9506afbe07..66dd7ce451e6d 100644 --- a/sdk/core/Azure.Core.Experimental/tests/public/JsonDataPublicTests.cs +++ b/sdk/core/Azure.Core.Experimental/tests/public/JsonDataPublicTests.cs @@ -92,16 +92,17 @@ public void DynamicCanConvertToIEnumerableInt() public void DynamicArrayHasLength() { dynamic jsonData = new BinaryData("[0, 1, 2, 3]").ToDynamic(); - Assert.AreEqual(4, jsonData.Length); + Assert.AreEqual(4, ((int[])jsonData).Length); } [Test] - public void DynamicArrayFor() + public void DynamicArrayForEach() { dynamic jsonData = new BinaryData("[0, 1, 2, 3]").ToDynamic(); - for (int i = 0; i < jsonData.Length; i++) + int expected = 0; + foreach (int i in jsonData) { - Assert.AreEqual(i, (int)jsonData[i]); + Assert.AreEqual(expected++, i); } } @@ -115,12 +116,13 @@ public void CanAccessProperties() } [Test] + [Ignore("To be implemented.")] public void CanTestPropertyForNull() { dynamic jsonData = new BinaryData("{ \"primitive\":\"Hello\", \"nested\": { \"nestedPrimitive\":true } }").ToDynamic(); - Assert.IsNull(jsonData.OptionalInt); - Assert.IsNull(jsonData.OptionalString); + Assert.IsNull((int?)jsonData.OptionalInt); + Assert.IsNull((string)jsonData.OptionalString); Assert.AreEqual("Hello", (string)jsonData.primitive); } @@ -186,14 +188,20 @@ public void ReadingFloatingPointAsIntThrows() } [Test] + [Ignore("Decide how to handle this case.")] public void FloatOverflowThrows() { var json = new BinaryData("34028234663852885981170418348451692544000").ToDynamic(); + + JsonDocument doc = JsonDocument.Parse("34028234663852885981170418348451692544000"); + float f = doc.RootElement.GetSingle(); + dynamic jsonData = json; - Assert.Throws(() => _ = (float)json); - Assert.Throws(() => _ = (float)jsonData); Assert.AreEqual(34028234663852885981170418348451692544000d, (double)jsonData); Assert.AreEqual(34028234663852885981170418348451692544000d, (double)json); + Assert.Throws(() => _ = (float)34028234663852885981170418348451692544000d); + Assert.Throws(() => _ = (float)json); + Assert.Throws(() => _ = (float)jsonData); } [Test] @@ -215,6 +223,7 @@ public void CanAccessJsonPropertiesWithDotnetIllegalCharacters() } [Test] + [Ignore("Decide how to handle this case.")] public void FloatUnderflowThrows() { var json = new BinaryData("-34028234663852885981170418348451692544000").ToDynamic(); @@ -230,8 +239,8 @@ public void IntOverflowThrows() { var json = new BinaryData("3402823466385288598").ToDynamic(); dynamic jsonData = json; - Assert.Throws(() => _ = (int)json); - Assert.Throws(() => _ = (int)jsonData); + Assert.Throws(() => _ = (int)json); + Assert.Throws(() => _ = (int)jsonData); Assert.AreEqual(3402823466385288598L, (long)jsonData); Assert.AreEqual(3402823466385288598L, (long)json); Assert.AreEqual(3402823466385288598D, (double)jsonData); @@ -245,8 +254,8 @@ public void IntUnderflowThrows() { var json = new BinaryData("-3402823466385288598").ToDynamic(); dynamic jsonData = json; - Assert.Throws(() => _ = (int)json); - Assert.Throws(() => _ = (int)jsonData); + Assert.Throws(() => _ = (int)json); + Assert.Throws(() => _ = (int)jsonData); Assert.AreEqual(-3402823466385288598L, (long)jsonData); Assert.AreEqual(-3402823466385288598L, (long)json); Assert.AreEqual(-3402823466385288598D, (double)jsonData); @@ -302,24 +311,7 @@ public void CanCastToIEnumerableOfT() } [Test] - public void CanGetDynamicFromBinaryData() - { - var data = new BinaryData(new - { - array = new[] { 1, 2, 3 } - }); - - dynamic json = data.ToDynamic(); - dynamic array = json.array; - - int i = 0; - foreach (int item in array) - { - Assert.AreEqual(++i, item); - } - } - - [Test] + [Ignore("To be implemented.")] public void EqualsHandlesStringsSpecial() { dynamic json = new BinaryData("\"test\"").ToDynamic(); @@ -329,6 +321,7 @@ public void EqualsHandlesStringsSpecial() } [Test] + [Ignore("To be implemented.")] public void EqualsForObjectsAndArrays() { dynamic obj1 = new BinaryData(new { foo = "bar" }).ToDynamic(); @@ -338,8 +331,8 @@ public void EqualsForObjectsAndArrays() dynamic arr2 = new BinaryData(new[] { "bar" }).ToDynamic(); // For objects and arrays, Equals provides reference equality. - Assert.AreEqual(obj1, obj1); - Assert.AreEqual(arr1, arr1); + Assert.AreEqual(obj1, obj2); + Assert.AreEqual(arr1, arr2); Assert.AreNotEqual(obj1, obj2); Assert.AreNotEqual(arr1, arr2); @@ -477,7 +470,7 @@ public async Task CanWriteToStream() using var stream = new MemoryStream(); using (var writer = new Utf8JsonWriter(stream)) { - DynamicData.WriteTo(writer, json); + DynamicData.WriteTo(stream, json); } // Assert