< Summary

Class:Azure.Core.DynamicJson
Assembly:Azure.Core.Experimental
File(s):C:\Git\azure-sdk-for-net\sdk\core\Azure.Core.Experimental\src\DynamicJson.cs
Covered lines:177
Uncovered lines:47
Coverable lines:224
Total lines:590
Line coverage:79% (177 of 224)
Covered branches:72
Total branches:88
Branch coverage:81.8% (72 of 88)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.ctor(...)-0%100%
.ctor(...)-94.74%91.67%
.ctor(...)-66.67%50%
.ctor(...)-100%100%
.ctor(...)-100%100%
Parse(...)-100%100%
Create(...)-100%100%
WriteTo(...)-100%91.67%
get_Item(...)-100%100%
set_Item(...)-100%100%
get_Item(...)-0%100%
set_Item(...)-100%100%
SetValueAt(...)-100%100%
ToJsonElement()-0%100%
ToString()-100%100%
System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(...)-100%100%
GetDynamicEnumerable()-66.67%50%
GetPropertyValue(...)-66.67%50%
SetValue(...)-100%100%
EnsureArray()-66.67%50%
EnsureObject()-66.67%50%
EnsureValue()-66.67%50%
EnsureNumberValue()-100%100%
GetValueAt(...)-100%100%
.cctor()-100%100%
.ctor(...)-100%100%
BindGetMember(...)-100%100%
BindConvert(...)-100%100%
BindSetMember(...)-100%100%
EnumerateArray()-0%100%
EnumerateObject()-0%100%
Object()-100%100%
Object(...)-100%100%
Array()-0%100%
Array(...)-0%100%
Array(...)-100%100%
op_Explicit(...)-100%100%
op_Explicit(...)-100%100%
op_Explicit(...)-100%100%
op_Explicit(...)-100%100%
op_Explicit(...)-100%100%
op_Explicit(...)-100%100%
op_Explicit(...)-100%50%
op_Explicit(...)-100%50%
op_Explicit(...)-100%50%
op_Explicit(...)-0%0%
op_Explicit(...)-0%0%
op_Implicit(...)-100%100%
op_Implicit(...)-0%100%
op_Implicit(...)-0%100%
op_Implicit(...)-0%100%
op_Implicit(...)-0%100%
op_Implicit(...)-100%100%
op_Implicit(...)-0%100%
op_Implicit(...)-0%100%
op_Implicit(...)-0%100%
op_Implicit(...)-0%100%
op_Implicit(...)-0%100%
GetString()-100%100%
GetIn32()-100%100%
GetLong()-100%100%
GetFloat()-100%100%
GetDouble()-100%100%
GetBoolean()-100%100%
GetArrayLength()-100%100%
GetProperty(...)-0%100%
Serialize(...)-100%100%
Serialize(...)-0%100%
SerializeAsync()-0%100%
Deserialize(...)-0%100%
Deserialize(...)-0%100%
DeserializeAsync()-0%100%
.ctor(...)-100%100%
.ctor(...)-100%100%
.ctor(...)-100%100%
WriteTo(...)-100%100%
AsLong()-100%100%
AsDouble()-100%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\core\Azure.Core.Experimental\src\DynamicJson.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.Collections;
 6using System.Collections.Generic;
 7using System.Diagnostics;
 8using System.Dynamic;
 9using System.IO;
 10using System.Linq.Expressions;
 11using System.Reflection;
 12using System.Text;
 13using System.Text.Json;
 14using System.Threading;
 15using System.Threading.Tasks;
 16using Azure.Core.Serialization;
 17
 18#pragma warning disable 1591
 19
 20namespace Azure.Core
 21{
 22    /// <summary>
 23    ///
 24    /// </summary>
 25    public class DynamicJson : IDynamicMetaObjectProvider
 26    {
 27        private readonly JsonValueKind _kind;
 28        private Dictionary<string, DynamicJson>? _objectRepresentation;
 29        private List<DynamicJson>? _arrayRepresentation;
 30        private object? _value;
 31
 032        public DynamicJson(string json): this(JsonDocument.Parse(json).RootElement)
 33        {
 034        }
 35
 36        /// <summary>
 37        ///
 38        /// </summary>
 39        /// <param name="element"></param>
 25640        public DynamicJson(JsonElement element)
 41        {
 25642            _kind = element.ValueKind;
 25643            switch (element.ValueKind)
 44            {
 45                case JsonValueKind.Object:
 9846                    _objectRepresentation = new Dictionary<string, DynamicJson>();
 35247                    foreach (var item in element.EnumerateObject())
 48                    {
 7849                        _objectRepresentation[item.Name] = new DynamicJson(item.Value);
 50                    }
 51                    break;
 52                case JsonValueKind.Array:
 1653                    _arrayRepresentation = new List<DynamicJson>();
 14054                    foreach (var item in element.EnumerateArray())
 55                    {
 5456                        _arrayRepresentation.Add(new DynamicJson(item));
 57                    }
 58                    break;
 59                case JsonValueKind.String:
 1460                    _value = element.GetString();
 1461                    break;
 62                case JsonValueKind.Number:
 10663                    _value = new Number(element);
 10664                    break;
 65                case JsonValueKind.True:
 66                case JsonValueKind.False:
 1267                    _value = element.GetBoolean();
 1268                    break;
 69                case JsonValueKind.Null:
 1070                    _value = null;
 1071                    break;
 72                default:
 073                    throw new ArgumentOutOfRangeException(nameof(element), "Unsupported element kind");
 74            }
 11475        }
 76
 477        private DynamicJson(IEnumerable<KeyValuePair<string,DynamicJson>> properties)
 78        {
 479            _kind = JsonValueKind.Object;
 480            _objectRepresentation = new Dictionary<string, DynamicJson>();
 081            foreach (var property in properties)
 82            {
 083                _objectRepresentation[property.Key] = property.Value;
 84            }
 485        }
 86
 287        private DynamicJson(IEnumerable<DynamicJson> array)
 88        {
 289            _kind = JsonValueKind.Array;
 290            _arrayRepresentation = new List<DynamicJson>();
 2091            foreach (var item in array)
 92            {
 893                if (item == null)
 94                {
 295                    _arrayRepresentation.Add(new DynamicJson((object?)null));
 96                }
 97                else
 98                {
 699                    _arrayRepresentation.Add(item);
 100                }
 101            }
 2102        }
 103
 60104        private DynamicJson(object? value)
 105        {
 60106            _value = value;
 60107            switch (value)
 108            {
 109                case long l:
 4110                    _kind = JsonValueKind.Number;
 4111                    _value = new Number(l);
 4112                    break;
 113                case int i:
 16114                    _kind = JsonValueKind.Number;
 16115                    _value = new Number(i);
 16116                    break;
 117                case double d:
 8118                    _kind = JsonValueKind.Number;
 8119                    _value = new Number(d);
 8120                    break;
 121                case float d:
 4122                    _kind = JsonValueKind.Number;
 4123                    _value = new Number(d);
 4124                    break;
 8125                case bool b when b:
 4126                    _kind = JsonValueKind.True;
 4127                    break;
 4128                case bool b when !b:
 4129                    _kind = JsonValueKind.False;
 4130                    break;
 131                default:
 20132                    _kind = value == null ? JsonValueKind.Null : JsonValueKind.String;
 133                    break;
 134            }
 20135        }
 136
 137        public static DynamicJson Parse(string json)
 138        {
 122139            return Create(JsonDocument.Parse(json).RootElement);
 140        }
 141
 142        /// <summary>
 143        ///
 144        /// </summary>
 145        /// <param name="element"></param>
 146        /// <returns></returns>
 147        public static DynamicJson Create(JsonElement element)
 148        {
 122149            return new DynamicJson(element);
 150        }
 151
 152        /// <summary>
 153        ///
 154        /// </summary>
 155        /// <param name="writer"></param>
 156        /// <exception cref="ArgumentOutOfRangeException"></exception>
 157        public void WriteTo(Utf8JsonWriter writer)
 158        {
 194159            switch (_kind)
 160            {
 161                case JsonValueKind.Null:
 162                case JsonValueKind.String:
 26163                    writer.WriteStringValue((string?)_value);
 26164                    break;
 165                case JsonValueKind.Number:
 50166                    ((Number) _value!).WriteTo(writer);
 50167                    break;
 168                case JsonValueKind.True:
 169                case JsonValueKind.False:
 12170                    writer.WriteBooleanValue((bool)_value!);
 12171                    break;
 172                case JsonValueKind.Object:
 98173                    writer.WriteStartObject();
 396174                    foreach (var property in EnsureObject())
 175                    {
 100176                        writer.WritePropertyName(property.Key);
 100177                        property.Value.WriteTo(writer);
 178                    }
 98179                    writer.WriteEndObject();
 98180                    break;
 181                case JsonValueKind.Array:
 8182                    writer.WriteStartArray();
 72183                    foreach (var item in EnsureArray())
 184                    {
 28185                        item.WriteTo(writer);
 186                    }
 8187                    writer.WriteEndArray();
 188                    break;
 189            }
 8190        }
 191
 192        /// <summary>
 193        ///
 194        /// </summary>
 195        /// <param name="arrayIndex"></param>
 196        public DynamicJson this[int arrayIndex]
 197        {
 8198            get => GetValueAt(arrayIndex);
 12199            set => SetValueAt(arrayIndex, value);
 200        }
 201
 202        /// <summary>
 203        ///
 204        /// </summary>
 205        /// <param name="propertyName"></param>
 206        public DynamicJson this[string propertyName]
 207        {
 0208            get => GetPropertyValue(propertyName);
 2209            set => SetValue(propertyName, value);
 210        }
 211
 212        private object SetValueAt(int index, object value)
 213        {
 12214            if (!(value is DynamicJson dynamicJson))
 215            {
 4216                dynamicJson = new DynamicJson(value);
 217            }
 12218            EnsureArray()[index] = dynamicJson;
 12219            return value;
 220        }
 221
 222        /// <summary>
 223        ///
 224        /// </summary>
 225        /// <returns></returns>
 226        public JsonElement ToJsonElement()
 227        {
 0228            var memoryStream = new MemoryStream();
 0229            var writer = new Utf8JsonWriter(memoryStream);
 0230            WriteTo(writer);
 0231            return JsonDocument.Parse(memoryStream.ToArray()).RootElement;
 232        }
 233
 234        /// <inheritdoc />
 235        public override string ToString()
 236        {
 66237            using var memoryStream = new MemoryStream();
 66238            using (var writer = new Utf8JsonWriter(memoryStream))
 239            {
 66240                WriteTo(writer);
 66241            }
 66242            return Encoding.UTF8.GetString(memoryStream.ToArray());
 66243        }
 244
 245        /// <inheritdoc />
 234246        DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) => new MetaObject(parameter, th
 247
 248        private IEnumerable GetDynamicEnumerable()
 249        {
 4250            if (_kind == JsonValueKind.Array)
 251            {
 4252                return EnsureArray();
 253            }
 254
 0255            return EnsureObject();
 256        }
 257
 258        private DynamicJson GetPropertyValue(string propertyName)
 259        {
 260
 88261            if (EnsureObject().TryGetValue(propertyName, out DynamicJson element))
 262            {
 88263                return element;
 264            }
 265
 0266            throw new InvalidOperationException($"Property {propertyName} not found");
 267        }
 268
 269        private object? SetValue(string propertyName, object? value)
 270        {
 48271            if (!(value is DynamicJson json))
 272            {
 38273                json = new DynamicJson(value);
 274            }
 275
 48276            EnsureObject()[propertyName] = json;
 48277            return value;
 278        }
 279
 280        private List<DynamicJson> EnsureArray()
 281        {
 44282            if (_kind != JsonValueKind.Array)
 283            {
 0284                throw new InvalidOperationException($"Expected kind to be array but was {_kind} instead");
 285            }
 286
 287            Debug.Assert(_arrayRepresentation != null);
 44288            return _arrayRepresentation!;
 289        }
 290
 291        private Dictionary<string, DynamicJson> EnsureObject()
 292        {
 234293            if (_kind != JsonValueKind.Object)
 294            {
 0295                throw new InvalidOperationException($"Expected kind to be object but was {_kind} instead");
 296            }
 297
 298            Debug.Assert(_objectRepresentation != null);
 234299            return _objectRepresentation!;
 300        }
 301
 302        private object? EnsureValue()
 303        {
 156304            if (_kind == JsonValueKind.Object || _kind == JsonValueKind.Array)
 305            {
 0306                throw new InvalidOperationException($"Expected kind to be value but was {_kind} instead");
 307            }
 308
 156309            return _value;
 310        }
 311
 312        private Number EnsureNumberValue()
 313        {
 128314            if (_kind != JsonValueKind.Number)
 315            {
 4316                throw new InvalidOperationException($"Expected kind to be number but was {_kind} instead");
 317            }
 318
 124319            return (Number) EnsureValue()!;
 320        }
 321
 322        private DynamicJson GetValueAt(int index)
 323        {
 8324            return EnsureArray()[index];
 325        }
 326
 327        private class MetaObject : DynamicMetaObject
 328        {
 2329            private static readonly MethodInfo GetDynamicValueMethod = typeof(DynamicJson).GetMethod(nameof(GetPropertyV
 330
 2331            private static readonly MethodInfo GetDynamicEnumerableMethod = typeof(DynamicJson).GetMethod(nameof(GetDyna
 332
 234333            internal MetaObject(Expression parameter, IDynamicMetaObjectProvider value) : base(parameter, BindingRestric
 334            {
 234335            }
 336
 337            public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
 338            {
 68339                var targetObject = Expression.Convert(Expression, LimitType);
 340
 68341                var arguments = new Expression[] { Expression.Constant(binder.Name) };
 68342                var getPropertyCall = Expression.Call(targetObject, GetDynamicValueMethod, arguments);
 343
 68344                var restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType);
 68345                return new DynamicMetaObject(getPropertyCall, restrictions);
 346            }
 347
 348            public override DynamicMetaObject BindConvert(ConvertBinder binder)
 349            {
 106350                if (binder.Type == typeof(IEnumerable))
 351                {
 4352                    var targetObject = Expression.Convert(Expression, LimitType);
 4353                    var getPropertyCall = Expression.Call(targetObject, GetDynamicEnumerableMethod);
 354
 4355                    var restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType);
 4356                    return new DynamicMetaObject(getPropertyCall, restrictions);
 357                }
 102358                return base.BindConvert(binder);
 359            }
 360
 361            public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
 362            {
 38363                Expression targetObject = Expression.Convert(Expression, LimitType);
 38364                var methodImplementation = typeof(DynamicJson).GetMethod(nameof(SetValue), BindingFlags.NonPublic | Bind
 38365                var arguments = new Expression[2] { Expression.Constant(binder.Name), Expression.Convert(value.Expressio
 366
 38367                Expression setPropertyCall = Expression.Call(targetObject, methodImplementation, arguments);
 38368                BindingRestrictions restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType);
 38369                DynamicMetaObject setProperty = new DynamicMetaObject(setPropertyCall, restrictions);
 38370                return setProperty;
 371            }
 372        }
 373
 374
 375        /// <summary>
 376        ///
 377        /// </summary>
 378        /// <returns></returns>
 379        public IEnumerable<DynamicJson> EnumerateArray()
 380        {
 0381            return EnsureArray();
 382        }
 383
 384        /// <summary>
 385        ///
 386        /// </summary>
 387        /// <returns></returns>
 388        public IEnumerable<KeyValuePair<string, DynamicJson>> EnumerateObject()
 389        {
 0390            return EnsureObject();
 391        }
 392
 393        /// <summary>
 394        ///
 395        /// </summary>
 396        /// <returns></returns>
 397#pragma warning disable CA1720 // Identifier 'Object' contains type name
 398        public static DynamicJson Object()
 399        {
 4400            return Object(System.Array.Empty<KeyValuePair<string, DynamicJson>>());
 401        }
 402
 403        /// <summary>
 404        ///
 405        /// </summary>
 406        /// <returns></returns>
 407        public static DynamicJson Object(IEnumerable<KeyValuePair<string, DynamicJson>> values)
 408        {
 4409            return new DynamicJson(values);
 410        }
 411#pragma warning restore CA1720
 412
 413        /// <summary>
 414        ///
 415        /// </summary>
 416        /// <returns></returns>
 417        public static DynamicJson Array()
 418        {
 0419            return Array(System.Array.Empty<DynamicJson>());
 420        }
 421
 422        /// <summary>
 423        ///
 424        /// </summary>
 425        /// <returns></returns>
 426        public static DynamicJson Array(IEnumerable<DynamicJson> values)
 427        {
 0428            return new DynamicJson(values);
 429        }
 430
 431        /// <summary>
 432        ///
 433        /// </summary>
 434        /// <param name="values"></param>
 435        /// <returns></returns>
 436        public static DynamicJson Array(params DynamicJson[] values)
 437        {
 2438            return new DynamicJson(values);
 439        }
 440
 16441        public static explicit operator bool(DynamicJson json) => json.GetBoolean();
 46442        public static explicit operator int(DynamicJson json) => json.GetIn32();
 24443        public static explicit operator long(DynamicJson json) => json.GetLong();
 16444        public static explicit operator string?(DynamicJson json) => json.GetString();
 26445        public static explicit operator float(DynamicJson json) => json.GetFloat();
 32446        public static explicit operator double(DynamicJson json) => json.GetDouble();
 447
 448
 2449        public static explicit operator bool?(DynamicJson json) => json._kind == JsonValueKind.Null ? (bool?)null : json
 2450        public static explicit operator int?(DynamicJson json) => json._kind == JsonValueKind.Null ? (int?)null : json.G
 2451        public static explicit operator long?(DynamicJson json) => json._kind == JsonValueKind.Null ? (long?)null : json
 0452        public static explicit operator float?(DynamicJson json) => json._kind == JsonValueKind.Null ? (float?)null : js
 0453        public static explicit operator double?(DynamicJson json) => json._kind == JsonValueKind.Null ? (double?)null : 
 454
 8455        public static implicit operator DynamicJson(int value) => new DynamicJson(value);
 0456        public static implicit operator DynamicJson(long value) => new DynamicJson(value);
 0457        public static implicit operator DynamicJson(double value) => new DynamicJson(value);
 0458        public static implicit operator DynamicJson(float value) => new DynamicJson(value);
 0459        public static implicit operator DynamicJson(bool value) => new DynamicJson(value);
 8460        public static implicit operator DynamicJson(string? value) => new DynamicJson((object?)value);
 0461        public static implicit operator DynamicJson(int? value) => new DynamicJson(value);
 0462        public static implicit operator DynamicJson(long? value) => new DynamicJson(value);
 0463        public static implicit operator DynamicJson(double? value) => new DynamicJson(value);
 0464        public static implicit operator DynamicJson(float? value) => new DynamicJson(value);
 0465        public static implicit operator DynamicJson(bool? value) => new DynamicJson(value);
 466
 16467        public string? GetString() => (string?)EnsureValue();
 468
 469        public int GetIn32()
 470        {
 46471            var value = EnsureNumberValue().AsLong();
 38472            if (value > int.MaxValue || value < int.MinValue)
 473            {
 8474                throw new OverflowException();
 475            }
 30476            return (int)value;
 477        }
 478
 24479        public long GetLong() => EnsureNumberValue().AsLong();
 480        public float GetFloat()
 481        {
 26482            var value = EnsureNumberValue().AsDouble();
 26483            if (value > float.MaxValue || value < float.MinValue)
 484            {
 8485                throw new OverflowException();
 486            }
 18487            return (float)value;
 488        }
 32489        public double GetDouble() => EnsureNumberValue().AsDouble();
 16490        public bool GetBoolean() => (bool)EnsureValue()!;
 12491        public int GetArrayLength() => EnsureArray().Count;
 0492        public DynamicJson GetProperty(string name) => GetPropertyValue(name);
 493
 494        public static DynamicJson Serialize<T>(T value, JsonSerializerOptions? options = null)
 495        {
 2496            var serialized = JsonSerializer.Serialize<T>(value, options);
 2497            return new DynamicJson(JsonDocument.Parse(serialized).RootElement);
 498        }
 499
 500        public static DynamicJson Serialize<T>(T value, ObjectSerializer serializer, CancellationToken cancellationToken
 501        {
 0502            using var memoryStream = new MemoryStream();
 0503            serializer.Serialize(memoryStream, value, typeof(T), cancellationToken);
 0504            memoryStream.Position = 0;
 0505            return new DynamicJson(JsonDocument.Parse(memoryStream).RootElement);
 0506        }
 507
 508        public static async Task<DynamicJson> SerializeAsync<T>(T value, ObjectSerializer serializer, CancellationToken 
 509        {
 0510            using var memoryStream = new MemoryStream();
 0511            await serializer.SerializeAsync(memoryStream, value, typeof(T), cancellationToken).ConfigureAwait(false);
 0512            memoryStream.Position = 0;
 0513            return new DynamicJson(JsonDocument.Parse(memoryStream).RootElement);
 0514        }
 515
 516        public T Deserialize<T>(JsonSerializerOptions? options = null)
 517        {
 0518            return JsonSerializer.Deserialize<T>(ToString(), options);
 519        }
 520
 521        public T Deserialize<T>(ObjectSerializer serializer, CancellationToken cancellationToken = default)
 522        {
 0523            var stream = new MemoryStream(Encoding.UTF8.GetBytes(ToString()));
 0524            return (T) serializer.Deserialize(stream, typeof(T), cancellationToken);
 525        }
 526
 527        public async Task<T> DeserializeAsync<T>(ObjectSerializer serializer, CancellationToken cancellationToken = defa
 528        {
 0529            var stream = new MemoryStream(Encoding.UTF8.GetBytes(ToString()));
 0530            return (T) await serializer.DeserializeAsync(stream, typeof(T), cancellationToken).ConfigureAwait(false);
 0531        }
 532
 533        private struct Number
 534        {
 535            public Number(in JsonElement element)
 536            {
 106537                _hasDouble = element.TryGetDouble(out _double);
 106538                _hasLong = element.TryGetInt64(out _long);
 106539            }
 540
 541            public Number(long l)
 542            {
 20543                _long = l;
 20544                _hasLong = true;
 20545                _double = default;
 20546                _hasDouble = false;
 20547            }
 548
 549            private long _long;
 550            private bool _hasLong;
 551            private double _double;
 552            private bool _hasDouble;
 553
 554            public Number(double d)
 555            {
 12556                _long = default;
 12557                _hasLong = false;
 12558                _double = d;
 12559                _hasDouble = true;
 12560            }
 561
 562            public void WriteTo(Utf8JsonWriter writer)
 563            {
 50564                if (_hasDouble)
 565                {
 26566                    writer.WriteNumberValue(_double);
 567                }
 568                else
 569                {
 24570                    writer.WriteNumberValue(_long);
 571                }
 24572            }
 573
 574            public long AsLong()
 575            {
 66576                if (!_hasLong)
 577                {
 8578                    throw new FormatException();
 579                }
 58580                return _long;
 581            }
 582
 583            public double AsDouble()
 584            {
 58585                return _double;
 586            }
 587        }
 588
 589    }
 590}