Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Set(object) API from MutableJsonElement #38266

Merged
merged 15 commits into from
Aug 22, 2023
145 changes: 136 additions & 9 deletions sdk/core/Azure.Core/src/DynamicData/DynamicData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,12 @@ private IEnumerable GetEnumerable()
if (_options.PropertyNameFormat == JsonPropertyNames.UseExact ||
_element.TryGetProperty(name, out MutableJsonElement _))
{
_element = _element.SetProperty(name, value);
SetPropertyInternal(name, value);
return null;
}

// The dynamic content has a specified property name format.
_element = _element.SetProperty(FormatPropertyName(name), value);
SetPropertyInternal(FormatPropertyName(name), value);

// Binding machinery expects the call site signature to return an object
return null;
Expand All @@ -166,11 +166,8 @@ private IEnumerable GetEnumerable()
_ => false
};

private object ConvertType(object value)
{
byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(value, _serializerOptions);
return JsonDocument.Parse(bytes);
}
private JsonElement ConvertType(object value) =>
MutableJsonElement.SerializeToJsonElement(value, _serializerOptions);

private object? SetViaIndexer(object index, object value)
{
Expand All @@ -179,17 +176,147 @@ private object ConvertType(object value)
switch (index)
{
case string propertyName:
_element = _element.SetProperty(propertyName, value);
SetPropertyInternal(propertyName, value);
return null;
case int arrayIndex:
MutableJsonElement element = _element.GetIndexElement(arrayIndex);
element.Set(value);
SetInternal(ref element, value);
return new DynamicData(element, _options);
}

throw new InvalidOperationException($"Tried to access indexer with an unsupported index type: {index}");
}

private void SetPropertyInternal(string name, object value)
{
switch (value)
{
case bool b:
_element = _element.SetProperty(name, b);
break;
case string s:
_element = _element.SetProperty(name, s);
break;
case byte b:
_element = _element.SetProperty(name, b);
break;
case sbyte sb:
_element = _element.SetProperty(name, sb);
break;
case short sh:
_element = _element.SetProperty(name, sh);
break;
case ushort us:
_element = _element.SetProperty(name, us);
break;
case int i:
_element = _element.SetProperty(name, i);
break;
case uint u:
_element = _element.SetProperty(name, u);
break;
case long l:
_element = _element.SetProperty(name, l);
break;
case ulong ul:
_element = _element.SetProperty(name, ul);
break;
case float f:
_element = _element.SetProperty(name, f);
break;
case double d:
_element = _element.SetProperty(name, d);
break;
case decimal d:
_element = _element.SetProperty(name, d);
break;
case DateTime d:
_element = _element.SetProperty(name, d);
break;
case DateTimeOffset d:
_element = _element.SetProperty(name, d);
break;
case Guid g:
_element = _element.SetProperty(name, g);
break;
case null:
_element = _element.SetPropertyNull(name);
break;
case JsonElement e:
_element = _element.SetProperty(name, e);
break;
default:
JsonElement element = ConvertType(value);
_element = _element.SetProperty(name, element);
break;
}
}

private void SetInternal(ref MutableJsonElement element, object value)
{
switch (value)
{
case bool b:
element.Set(b);
break;
case string s:
element.Set(s);
break;
case byte b:
element.Set(b);
break;
case sbyte sb:
element.Set(sb);
break;
case short sh:
element.Set(sh);
break;
case ushort us:
element.Set(us);
break;
case int i:
element.Set(i);
break;
case uint u:
element.Set(u);
break;
case long l:
element.Set(l);
break;
case ulong ul:
element.Set(ul);
break;
case float f:
element.Set(f);
break;
case double d:
element.Set(d);
break;
case decimal d:
element.Set(d);
break;
case DateTime d:
element.Set(d);
break;
case DateTimeOffset d:
element.Set(d);
break;
case Guid g:
element.Set(g);
break;
case null:
element.SetNull();
break;
case JsonElement e:
element.Set(e);
break;
default:
JsonElement jsonElement = ConvertType(value);
element.Set(jsonElement);
break;
}
}

private T? ConvertTo<T>()
{
JsonElement element = _element.GetJsonElement();
Expand Down
82 changes: 58 additions & 24 deletions sdk/core/Azure.Core/src/DynamicData/MutableJsonChange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,23 @@
// Licensed under the MIT License.

using System;
using System.Buffers;
using System.Text.Json;

namespace Azure.Core.Json
{
internal struct MutableJsonChange
{
private JsonElement? _serializedValue;
private readonly JsonSerializerOptions _serializerOptions;

public MutableJsonChange(string path,
int index,
object? value,
JsonSerializerOptions options,
MutableJsonChangeKind changeKind,
string? addedPropertyName)
{
Path = path;
Index = index;
Value = value;
_serializerOptions = options;
ChangeKind = changeKind;
AddedPropertyName = addedPropertyName;

if (value is JsonElement element)
{
_serializedValue = element;
}

if (value is JsonDocument doc)
{
_serializedValue = doc.RootElement;
}
}

public string Path { get; }
Expand All @@ -47,18 +31,63 @@ public MutableJsonChange(string path,

public MutableJsonChangeKind ChangeKind { get; }

public JsonValueKind ValueKind => GetSerializedValue().ValueKind;
public readonly JsonValueKind ValueKind => Value switch
annelo-msft marked this conversation as resolved.
Show resolved Hide resolved
{
null => JsonValueKind.Null,
bool b => b ? JsonValueKind.True : JsonValueKind.False,
string => JsonValueKind.String,
DateTime => JsonValueKind.String,
DateTimeOffset => JsonValueKind.String,
Guid => JsonValueKind.String,
byte => JsonValueKind.Number,
sbyte => JsonValueKind.Number,
short => JsonValueKind.Number,
ushort => JsonValueKind.Number,
int => JsonValueKind.Number,
uint => JsonValueKind.Number,
long => JsonValueKind.Number,
ulong => JsonValueKind.Number,
float => JsonValueKind.Number,
double => JsonValueKind.Number,
decimal => JsonValueKind.Number,
JsonElement e => e.ValueKind,
_ => throw new InvalidOperationException($"Unrecognized change type '{Value.GetType()}'.")
};

internal readonly void EnsureString()
annelo-msft marked this conversation as resolved.
Show resolved Hide resolved
{
if (ValueKind != JsonValueKind.String)
{
throw new InvalidOperationException($"Expected a 'String' kind but was '{ValueKind}'.");
}
}

internal readonly void EnsureNumber()
{
if (ValueKind != JsonValueKind.Number)
{
throw new InvalidOperationException($"Expected a 'Number' kind but was '{ValueKind}'.");
}
}

internal JsonElement GetSerializedValue()
internal readonly void EnsureArray()
{
if (_serializedValue != null)
if (ValueKind != JsonValueKind.Array)
{
return _serializedValue.Value;
throw new InvalidOperationException($"Expected an 'Array' kind but was '{ValueKind}'.");
}
}

internal readonly int GetArrayLength()
{
EnsureArray();

byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(Value, _serializerOptions);
_serializedValue = JsonDocument.Parse(bytes).RootElement;
return _serializedValue.Value;
if (Value is JsonElement e)
{
return e.GetArrayLength();
}

throw new InvalidOperationException($"Expected an 'Array' kind but was '{ValueKind}'.");
}

internal bool IsDescendant(string path)
Expand Down Expand Up @@ -110,7 +139,12 @@ internal bool IsGreaterThan(ReadOnlySpan<char> otherPath)

internal string AsString()
{
return GetSerializedValue().ToString() ?? "null";
if (Value is null)
{
return "null";
}

return Value.ToString()!;
}

public override string ToString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,14 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.Json;

namespace Azure.Core.Json
{
internal partial class MutableJsonDocument
{
internal class ChangeTracker
{
public ChangeTracker(JsonSerializerOptions options)
{
_options = options;
}

private List<MutableJsonChange>? _changes;
private JsonSerializerOptions _options;

internal const char Delimiter = (char)1;

Expand Down Expand Up @@ -100,7 +93,7 @@ internal int AddChange(string path, object? value, MutableJsonChangeKind changeK

int index = _changes.Count;

_changes.Add(new MutableJsonChange(path, index, value, _options, changeKind, addedPropertyName));
_changes.Add(new MutableJsonChange(path, index, value, changeKind, addedPropertyName));

return index;
}
Expand Down
8 changes: 5 additions & 3 deletions sdk/core/Azure.Core/src/DynamicData/MutableJsonDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,20 @@ namespace Azure.Core.Json
[JsonConverter(typeof(MutableJsonDocumentConverter))]
internal sealed partial class MutableJsonDocument : IDisposable
{
private static readonly JsonSerializerOptions DefaultSerializerOptions = new JsonSerializerOptions();

private readonly ReadOnlyMemory<byte> _original;
private readonly JsonDocument _originalDocument;

private readonly JsonSerializerOptions _serializerOptions;
internal JsonSerializerOptions SerializerOptions { get => _serializerOptions; }
internal JsonSerializerOptions SerializerOptions => _serializerOptions;

private ChangeTracker? _changeTracker;
internal ChangeTracker Changes
{
get
{
_changeTracker ??= new ChangeTracker(SerializerOptions);
_changeTracker ??= new();
return _changeTracker;
}
}
Expand Down Expand Up @@ -203,7 +205,7 @@ private MutableJsonDocument(JsonDocument document, ReadOnlyMemory<byte> utf8Json
{
_originalDocument = document;
_original = utf8Json;
_serializerOptions = serializerOptions ?? new JsonSerializerOptions();
_serializerOptions = serializerOptions ?? DefaultSerializerOptions;
}

private class MutableJsonDocumentConverter : JsonConverter<MutableJsonDocument>
Expand Down
Loading