Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ protected override MethodProvider[] BuildMethods()
BuildWriteNumberValueMethodProvider(),
BuildWriteObjectValueMethodGeneric(),
BuildWriteObjectValueMethodProvider(),
BuildGetUtf8BytesMethodProvider(),
.. BuildDynamicModelHelpers()
];
}
Expand Down Expand Up @@ -576,7 +577,6 @@ private MethodProvider[] BuildDynamicModelHelpers()
[
BuildSliceToStartOfPropertyNameMethodProvider(),
BuildGetFirstPropertyNameMethodProvider(),
BuildGetUtf8BytesMethodProvider(),
BuildTryGetIndexMethodProvider(),
BuildGetRemainderMethodProvider()
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ private MethodBodyStatement[] BuildDeserializationMethodBody()
// Build the deserialization statements for each property
ForEachStatement deserializePropertiesForEachStatement = new("prop", _jsonElementParameterSnippet.EnumerateObject(), out var prop)
{
BuildDeserializePropertiesStatements(prop.As<JsonProperty>(), _dataParameter.As<BinaryData>())
BuildDeserializePropertiesStatements(prop.As<JsonProperty>())
};

var valueKindEqualsNullReturn = _isStruct ? Return(Default) : Return(Null);
Expand Down Expand Up @@ -910,7 +910,7 @@ private static ValueExpression GetValueForSerializationConstructor(PropertyProvi
return propertyProvider.AsVariableExpression;
}

private List<MethodBodyStatement> BuildDeserializePropertiesStatements(ScopedApi<JsonProperty> jsonProperty, ScopedApi<BinaryData> data)
private List<MethodBodyStatement> BuildDeserializePropertiesStatements(ScopedApi<JsonProperty> jsonProperty)
{
List<MethodBodyStatement> propertyDeserializationStatements = [];
Dictionary<JsonValueKind, List<MethodBodyStatement>> additionalPropsValueKindBodyStatements = [];
Expand Down Expand Up @@ -943,15 +943,15 @@ private List<MethodBodyStatement> BuildDeserializePropertiesStatements(ScopedApi
// handle additional properties
if (parameter.Property != null && parameter.Property != _additionalBinaryDataProperty.Value && parameter.Property.IsAdditionalProperties)
{
AddAdditionalPropertiesValueKindStatements(additionalPropsValueKindBodyStatements, parameter.Property, jsonProperty, data);
AddAdditionalPropertiesValueKindStatements(additionalPropsValueKindBodyStatements, parameter.Property, jsonProperty);
continue;
}

var wireInfo = parameter.Property?.WireInfo ?? parameter.Field?.WireInfo;

// By default, we should only deserialize properties with wire info that are payload properties.
// Those properties without wire info indicate they are not spec properties.
if (wireInfo == null || wireInfo.IsHttpMetadata == true)
if (wireInfo == null || wireInfo.IsHttpMetadata)
{
continue;
}
Expand Down Expand Up @@ -998,7 +998,7 @@ private List<MethodBodyStatement> BuildDeserializePropertiesStatements(ScopedApi
if (_additionalBinaryDataProperty.Value != null)
{
var binaryDataDeserializationValue = ScmCodeModelGenerator.Instance.TypeFactory.DeserializeJsonValue(
_additionalBinaryDataProperty.Value.Type.ElementType.FrameworkType, jsonProperty.Value(), _dataParameter.As<BinaryData>(), _mrwOptionsParameterSnippet, SerializationFormat.Default);
_additionalBinaryDataProperty.Value.Type.ElementType, jsonProperty.Value(), _dataParameter.As<BinaryData>(), _mrwOptionsParameterSnippet, SerializationFormat.Default);
propertyDeserializationStatements.Add(
_additionalBinaryDataProperty.Value.AsVariableExpression.AsDictionary(_additionalBinaryDataProperty.Value.Type).Add(jsonProperty.Name(), binaryDataDeserializationValue));
}
Expand Down Expand Up @@ -1028,8 +1028,7 @@ private List<MethodBodyStatement> BuildDeserializePropertiesStatements(ScopedApi
private void AddAdditionalPropertiesValueKindStatements(
Dictionary<JsonValueKind, List<MethodBodyStatement>> additionalPropsValueKindBodyStatements,
PropertyProvider additionalPropertiesProperty,
ScopedApi<JsonProperty> jsonProperty,
ScopedApi<BinaryData> data)
ScopedApi<JsonProperty> jsonProperty)
{
DictionaryExpression additionalPropsDict = additionalPropertiesProperty.AsVariableExpression.AsDictionary(additionalPropertiesProperty.Type);
var valueType = additionalPropertiesProperty.Type.ElementType;
Expand Down Expand Up @@ -1424,22 +1423,36 @@ private MethodBodyStatement DeserializeValue(
}
else
{
value = CreateDeserializeValueExpression(valueType, serializationFormat, jsonElement, jsonElement.GetUtf8Bytes());
value = CreateDeserializeValueExpression(valueType, serializationFormat, jsonElement);
return MethodBodyStatement.Empty;
}
}

private ValueExpression CreateDeserializeValueExpression(CSharpType valueType, SerializationFormat serializationFormat, ScopedApi<JsonElement> jsonElement, ScopedApi<BinaryData> data) =>
valueType switch
private ValueExpression CreateDeserializeValueExpression(
CSharpType valueType,
SerializationFormat serializationFormat,
ScopedApi<JsonElement> jsonElement)
{
var data = jsonElement.GetUtf8Bytes();

return valueType switch
{
{ IsFrameworkType: true } when valueType.FrameworkType == typeof(Nullable<>) =>
ScmCodeModelGenerator.Instance.TypeFactory.DeserializeJsonValue(valueType.Arguments[0].FrameworkType, jsonElement, _dataParameter.As<BinaryData>(), _mrwOptionsParameterSnippet, serializationFormat),
ScmCodeModelGenerator.Instance.TypeFactory.DeserializeJsonValue(
valueType.Arguments[0].FrameworkType, jsonElement, data,
_mrwOptionsParameterSnippet, serializationFormat),
{ IsFrameworkType: true } =>
ScmCodeModelGenerator.Instance.TypeFactory.DeserializeJsonValue(valueType.FrameworkType, jsonElement, _dataParameter.As<BinaryData>(), _mrwOptionsParameterSnippet, serializationFormat),
ScmCodeModelGenerator.Instance.TypeFactory.DeserializeJsonValue(valueType.FrameworkType,
jsonElement, data, _mrwOptionsParameterSnippet,
serializationFormat),
{ IsEnum: true } =>
valueType.ToEnum(ScmCodeModelGenerator.Instance.TypeFactory.DeserializeJsonValue(valueType.UnderlyingEnumType!, jsonElement, _dataParameter.As<BinaryData>(), _mrwOptionsParameterSnippet, serializationFormat)),
_ => GetDeserializationMethodInvocationForType(valueType, jsonElement, data, _mrwOptionsParameterSnippet)
valueType.ToEnum(ScmCodeModelGenerator.Instance.TypeFactory.DeserializeJsonValue(
valueType.UnderlyingEnumType!, jsonElement, data,
_mrwOptionsParameterSnippet, serializationFormat)),
_ => GetDeserializationMethodInvocationForType(valueType, jsonElement, data,
_mrwOptionsParameterSnippet)
};
}

private MethodBodyStatement CreateDeserializeDictionaryValueStatement(
CSharpType dictionaryItemType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,25 @@ public void ValidateGetRequiredStringMethodIsGenerated()
Assert.AreEqual(typeof(JsonElement), getRequiredStringMethod.Signature.Parameters[0].Type.FrameworkType);
}

[Test]
public void ValidateGetUtf8BytesMethodIsGenerated()
{
MockHelpers.LoadMockGenerator();

var definition = new ModelSerializationExtensionsDefinition();
var methods = definition.Methods;

Assert.IsNotNull(methods);
var getUtf8BytesMethod = methods.SingleOrDefault(m => m.Signature.Name == "GetUtf8Bytes");
Assert.IsNotNull(getUtf8BytesMethod, "GetUtf8Bytes method should be generated");
Assert.AreEqual(typeof(BinaryData), getUtf8BytesMethod!.Signature.ReturnType?.FrameworkType);
Assert.IsTrue(getUtf8BytesMethod.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public));
Assert.IsTrue(getUtf8BytesMethod.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Static));
Assert.IsTrue(getUtf8BytesMethod.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Extension));
Assert.AreEqual(1, getUtf8BytesMethod.Signature.Parameters.Count);
Assert.AreEqual(typeof(JsonElement), getUtf8BytesMethod.Signature.Parameters[0].Type.FrameworkType);
}

[Test]
public void ValidateWriteStringValueMethodsAreGenerated()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,21 @@ public void SerializedNameIsUsed(bool isRequired)
Assert.AreEqual(Helpers.GetExpectedFromFile(isRequired.ToString()), methodBody);
}

[Test]
public void GetUtf8BytesIsUsedForMrwFallback()
{
var property = InputFactory.Property(
"mockProperty",
InputFactory.Model("SomeExternalModel", external: new InputExternalTypeMetadata("System.IO.File", null, null)));

var inputModel = InputFactory.Model("mockInputModel", properties: [property]);
var (_, serialization) = CreateModelAndSerialization(inputModel);

var deserializationMethod = serialization.Methods.Single(m => m.Signature.Name.StartsWith("Deserialize"));
var methodBody = deserializationMethod.BodyStatements!.ToDisplayString();
Assert.AreEqual(Helpers.GetExpectedFromFile(), methodBody);
}

[Test]
public void TestBuildDeserializationMethodNestedSARD()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
if ((element.ValueKind == global::System.Text.Json.JsonValueKind.Null))
{
return null;
}
global::System.IO.File mockProperty = default;
global::System.Collections.Generic.IDictionary<string, global::System.BinaryData> additionalBinaryDataProperties = new global::Sample.ChangeTrackingDictionary<string, global::System.BinaryData>();
foreach (var prop in element.EnumerateObject())
{
if (prop.NameEquals("mockProperty"u8))
{
if ((prop.Value.ValueKind == global::System.Text.Json.JsonValueKind.Null))
{
continue;
}
mockProperty = global::System.ClientModel.Primitives.ModelReaderWriter.Read<global::System.IO.File>(prop.Value.GetUtf8Bytes(), global::Sample.ModelSerializationExtensions.WireOptions, global::Sample.SampleContext.Default);
continue;
}
if ((options.Format != "W"))
{
additionalBinaryDataProperties.Add(prop.Name, global::System.BinaryData.FromString(prop.Value.GetRawText()));
}
}
return new global::Sample.Models.MockInputModel(mockProperty, additionalBinaryDataProperties);
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,15 @@ public static void WriteObjectValue(this Utf8JsonWriter writer, object value, Mo
writer.WriteObjectValue<object>(value, options);
}

public static BinaryData GetUtf8Bytes(this JsonElement element)
{
#if NET9_0_OR_GREATER
return new global::System.BinaryData(global::System.Runtime.InteropServices.JsonMarshal.GetRawUtf8Value(element).ToArray());
#else
return BinaryData.FromString(element.GetRawText());
#endif
}

public static ReadOnlySpan<byte> SliceToStartOfPropertyName(this ReadOnlySpan<byte> jsonPath)
{
ReadOnlySpan<byte> local = jsonPath;
Expand Down Expand Up @@ -304,15 +313,6 @@ public static string GetFirstPropertyName(this ReadOnlySpan<byte> jsonPath, out
return key;
}

public static BinaryData GetUtf8Bytes(this JsonElement element)
{
#if NET9_0_OR_GREATER
return new global::System.BinaryData(global::System.Runtime.InteropServices.JsonMarshal.GetRawUtf8Value(element).ToArray());
#else
return BinaryData.FromString(element.GetRawText());
#endif
}

public static bool TryGetIndex(this ReadOnlySpan<byte> indexSlice, out int index, out int bytesConsumed)
{
index = -1;
Expand Down
Loading