diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index c43f9c9a5f55cf..bb372b2e25d1ee 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -732,7 +732,7 @@ public abstract partial class JsonConverter internal JsonConverter() { } public abstract bool CanConvert(System.Type typeToConvert); } - [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Enum | System.AttributeTargets.Property | System.AttributeTargets.Struct, AllowMultiple=false)] + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Enum | System.AttributeTargets.Property | System.AttributeTargets.Field | System.AttributeTargets.Struct, AllowMultiple=false)] public partial class JsonConverterAttribute : System.Text.Json.Serialization.JsonAttribute { protected JsonConverterAttribute() { } @@ -752,17 +752,17 @@ protected internal JsonConverter() { } public abstract T Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options); public abstract void Write(System.Text.Json.Utf8JsonWriter writer, T value, System.Text.Json.JsonSerializerOptions options); } - [System.AttributeUsageAttribute(System.AttributeTargets.Property, AllowMultiple=false)] + [System.AttributeUsageAttribute(System.AttributeTargets.Property | System.AttributeTargets.Field, AllowMultiple=false)] public sealed partial class JsonExtensionDataAttribute : System.Text.Json.Serialization.JsonAttribute { public JsonExtensionDataAttribute() { } } - [System.AttributeUsageAttribute(System.AttributeTargets.Property, AllowMultiple=false)] + [System.AttributeUsageAttribute(System.AttributeTargets.Property | System.AttributeTargets.Field, AllowMultiple=false)] public sealed partial class JsonIgnoreAttribute : System.Text.Json.Serialization.JsonAttribute { public JsonIgnoreAttribute() { } } - [System.AttributeUsageAttribute(System.AttributeTargets.Property, AllowMultiple=false)] + [System.AttributeUsageAttribute(System.AttributeTargets.Property | System.AttributeTargets.Field, AllowMultiple=false)] public sealed partial class JsonPropertyNameAttribute : System.Text.Json.Serialization.JsonAttribute { public JsonPropertyNameAttribute(string name) { } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs index fb91bafd4e78ea..98a618d32c7271 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs @@ -13,7 +13,7 @@ namespace System.Text.Json { internal partial class JsonClassInfo { - private JsonPropertyInfo AddProperty(Type propertyType, PropertyInfo propertyInfo, Type parentClassType, JsonSerializerOptions options) + private JsonPropertyInfo AddProperty(Type propertyType, MemberInfo propertyInfo, Type parentClassType, JsonSerializerOptions options) { bool hasIgnoreAttribute = (JsonPropertyInfo.GetAttribute(propertyInfo) != null); if (hasIgnoreAttribute) @@ -50,7 +50,7 @@ private JsonPropertyInfo AddProperty(Type propertyType, PropertyInfo propertyInf internal static JsonPropertyInfo CreateProperty( Type declaredPropertyType, Type runtimePropertyType, - PropertyInfo? propertyInfo, + MemberInfo? propertyInfo, Type parentClassType, Type? collectionElementType, Type? nullableUnderlyingType, diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs index dcc2a92e18dd15..26a12c3ac1f69b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs @@ -139,6 +139,7 @@ public JsonClassInfo(Type type, JsonSerializerOptions options) CreateObject = options.MemberAccessorStrategy.CreateConstructor(type); PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); Dictionary cache = CreatePropertyCache(properties.Length); @@ -176,6 +177,35 @@ public JsonClassInfo(Type type, JsonSerializerOptions options) } } + foreach (FieldInfo fieldInfo in fields) + { + if (fieldInfo.GetCustomAttribute(typeof(CompilerGeneratedAttribute)) is { }) + { + // Ignore compiler generated fields + continue; + } + + JsonPropertyInfo jsonPropertyInfo = AddProperty(fieldInfo.FieldType, fieldInfo, type, options); + Debug.Assert(jsonPropertyInfo != null && jsonPropertyInfo.NameAsString != null); + + // If the JsonPropertyNameAttribute or naming policy results in collisions, throw an exception. + if (!JsonHelpers.TryAdd(cache, jsonPropertyInfo.NameAsString, jsonPropertyInfo)) + { + JsonPropertyInfo other = cache[jsonPropertyInfo.NameAsString]; + + if (other.ShouldDeserialize == false && other.ShouldSerialize == false) + { + // Overwrite the one just added since it has [JsonIgnore]. + cache[jsonPropertyInfo.NameAsString] = jsonPropertyInfo; + } + else if (jsonPropertyInfo.ShouldDeserialize == true || jsonPropertyInfo.ShouldSerialize == true) + { + ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(this, jsonPropertyInfo); + } + // else ignore jsonPropertyInfo since it has [JsonIgnore]. + } + } + JsonPropertyInfo[] cacheArray; if (DetermineExtensionDataProperty(cache)) { @@ -521,7 +551,7 @@ public static ulong GetKey(ReadOnlySpan propertyName) public static ClassType GetClassType( Type type, Type parentClassType, - PropertyInfo? propertyInfo, + MemberInfo? propertyInfo, out Type runtimeType, out Type? elementType, out Type? nullableUnderlyingType, diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterAttribute.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterAttribute.cs index 51095cd5c7b125..ef9d9fe53e9bef 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterAttribute.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterAttribute.cs @@ -14,7 +14,7 @@ namespace System.Text.Json.Serialization /// or there is another on a property /// of the same type. /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Struct, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Struct, AllowMultiple = false)] public class JsonConverterAttribute : JsonAttribute { /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonExtensionDataAttribute.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonExtensionDataAttribute.cs index 58013febe3a175..347f7e11365562 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonExtensionDataAttribute.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonExtensionDataAttribute.cs @@ -5,7 +5,7 @@ namespace System.Text.Json.Serialization { /// - /// When placed on a property of type , any + /// When placed on a property or a field of type , any /// properties that do not have a matching member are added to that Dictionary during deserialization and written during serialization. /// /// @@ -14,13 +14,13 @@ namespace System.Text.Json.Serialization /// During deserializing, when using a "null" JSON value is treated as a null object reference, and when using /// a "null" is treated as a JsonElement with set to . /// - /// During serializing, the name of the extension data property is not included in the JSON; + /// During serializing, the name of the extension data member is not included in the JSON; /// the data contained within the extension data is serialized as properties of the JSON object. /// - /// If there is more than one extension property on a type, or it the property is not of the correct type, + /// If there is more than one extension property or field on a type, or if the member is not of the correct type, /// an is thrown during the first serialization or deserialization of that type. /// - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] public sealed class JsonExtensionDataAttribute : JsonAttribute { } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonIgnoreAttribute.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonIgnoreAttribute.cs index 26fac42a90d59b..689d729f319585 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonIgnoreAttribute.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonIgnoreAttribute.cs @@ -5,9 +5,9 @@ namespace System.Text.Json.Serialization { /// - /// Prevents a property from being serialized or deserialized. + /// Prevents a property or a field from being serialized or deserialized. /// - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] public sealed class JsonIgnoreAttribute : JsonAttribute { /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs index 3b890e01aa1f85..fcdb308fdf14bb 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs @@ -62,7 +62,7 @@ public void CopyRuntimeSettingsTo(JsonPropertyInfo other) // Create a property that is ignored at run-time. It uses the same type (typeof(sbyte)) to help // prevent issues with unsupported types and helps ensure we don't accidently (de)serialize it. - public static JsonPropertyInfo CreateIgnoredPropertyPlaceholder(PropertyInfo? propertyInfo, JsonSerializerOptions options) + public static JsonPropertyInfo CreateIgnoredPropertyPlaceholder(MemberInfo? propertyInfo, JsonSerializerOptions options) { JsonPropertyInfo jsonPropertyInfo = new JsonPropertyInfoNotNullable(); jsonPropertyInfo.Options = options; @@ -238,7 +238,7 @@ public JsonClassInfo? ElementClassInfo // Use a field here (not a property) to avoid value semantics. public JsonEncodedText? EscapedName; - public static TAttribute? GetAttribute(PropertyInfo propertyInfo) where TAttribute : Attribute + public static TAttribute? GetAttribute(MemberInfo propertyInfo) where TAttribute : Attribute { return (TAttribute?)propertyInfo.GetCustomAttribute(typeof(TAttribute), inherit: false); } @@ -293,7 +293,7 @@ public virtual void Initialize( Type declaredPropertyType, Type runtimePropertyType, ClassType runtimeClassType, - PropertyInfo? propertyInfo, + MemberInfo? propertyInfo, Type? elementType, JsonConverter? converter, bool treatAsNullable, @@ -357,7 +357,7 @@ protected virtual void OnWriteDictionary(ref WriteStackFrame current, Utf8JsonWr public Type ParentClassType { get; private set; } = null!; - public PropertyInfo? PropertyInfo { get; private set; } + public MemberInfo? PropertyInfo { get; private set; } public void Read(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs index a67879610b5b26..e296ae73283c04 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.CompilerServices; using System.Text.Json.Serialization; namespace System.Text.Json @@ -26,7 +27,7 @@ public override void Initialize( Type declaredPropertyType, Type runtimePropertyType, ClassType runtimeClassType, - PropertyInfo? propertyInfo, + MemberInfo? propertyInfo, Type? elementType, JsonConverter? converter, bool treatAsNullable, @@ -45,17 +46,27 @@ public override void Initialize( if (propertyInfo != null) { - if (propertyInfo.GetMethod?.IsPublic == true) + Debug.Assert( + propertyInfo is PropertyInfo || + propertyInfo is FieldInfo); + + if (propertyInfo is PropertyInfo property) { - HasGetter = true; - Get = options.MemberAccessorStrategy.CreatePropertyGetter(propertyInfo); + Get = property.GetMethod?.IsPublic == true + ? options.MemberAccessorStrategy.CreateGetter(property) + : null; + Set = property.SetMethod?.IsPublic == true + ? options.MemberAccessorStrategy.CreateSetter(property) + : null; } - - if (propertyInfo.SetMethod?.IsPublic == true) + else { - HasSetter = true; - Set = options.MemberAccessorStrategy.CreatePropertySetter(propertyInfo); + Get = options.MemberAccessorStrategy.CreateGetter(Unsafe.As(propertyInfo)); + Set = options.MemberAccessorStrategy.CreateSetter(Unsafe.As(propertyInfo)); } + + HasGetter = Get is { }; + HasSetter = Set is { }; } else { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyNameAttribute.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyNameAttribute.cs index 563ed5d6fd3904..486866888245ff 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyNameAttribute.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyNameAttribute.cs @@ -8,7 +8,7 @@ namespace System.Text.Json.Serialization /// Specifies the property name that is present in the JSON when serializing and deserializing. /// This overrides any naming policy specified by . /// - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Property | System.AttributeTargets.Field, AllowMultiple = false)] public sealed class JsonPropertyNameAttribute : JsonAttribute { /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs index 161c1345c8af7b..f83704c27f1cba 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs @@ -65,7 +65,7 @@ private static List GetDefaultConverters() /// public IList Converters { get; } - internal JsonConverter? DetermineConverterForProperty(Type parentClassType, Type runtimePropertyType, PropertyInfo? propertyInfo) + internal JsonConverter? DetermineConverterForProperty(Type parentClassType, Type runtimePropertyType, MemberInfo? propertyInfo) { JsonConverter? converter = null; @@ -189,7 +189,7 @@ internal bool HasConverter(Type typeToConvert) return GetConverter(typeToConvert) != null; } - private JsonConverter GetConverterFromAttribute(JsonConverterAttribute converterAttribute, Type typeToConvert, Type classTypeAttributeIsOn, PropertyInfo? propertyInfo) + private JsonConverter GetConverterFromAttribute(JsonConverterAttribute converterAttribute, Type typeToConvert, Type classTypeAttributeIsOn, MemberInfo? propertyInfo) { JsonConverter? converter; @@ -223,7 +223,7 @@ private JsonConverter GetConverterFromAttribute(JsonConverterAttribute converter return converter; } - private static Attribute? GetAttributeThatCanHaveMultiple(Type classType, Type attributeType, PropertyInfo propertyInfo) + private static Attribute? GetAttributeThatCanHaveMultiple(Type classType, Type attributeType, MemberInfo propertyInfo) { object[] attributes = propertyInfo.GetCustomAttributes(attributeType, inherit: false); return GetAttributeThatCanHaveMultiple(attributeType, classType, propertyInfo, attributes); @@ -235,7 +235,7 @@ private JsonConverter GetConverterFromAttribute(JsonConverterAttribute converter return GetAttributeThatCanHaveMultiple(attributeType, classType, null, attributes); } - private static Attribute? GetAttributeThatCanHaveMultiple(Type attributeType, Type classType, PropertyInfo? propertyInfo, object[] attributes) + private static Attribute? GetAttributeThatCanHaveMultiple(Type attributeType, Type classType, MemberInfo? propertyInfo, object[] attributes) { if (attributes.Length == 0) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/MemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/MemberAccessor.cs index 1f34061f1f0021..5a5f98bfdbf67b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/MemberAccessor.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/MemberAccessor.cs @@ -50,8 +50,10 @@ private MethodInfo FindImmutableCreateRangeMethod(Type constructingType) return null!; } - public abstract Func CreatePropertyGetter(PropertyInfo propertyInfo); + public abstract Func CreateGetter(PropertyInfo propertyInfo); + public abstract Func CreateGetter(FieldInfo propertyInfo); - public abstract Action CreatePropertySetter(PropertyInfo propertyInfo); + public abstract Action CreateSetter(PropertyInfo propertyInfo); + public abstract Action CreateSetter(FieldInfo propertyInfo); } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMemberAccessor.cs index cd5135ea7959c6..c364eaeb8bafb7 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMemberAccessor.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMemberAccessor.cs @@ -141,22 +141,15 @@ public override ImmutableCollectionCreator ImmutableDictionaryCreateRange(Type c return creator; } - public override Func CreatePropertyGetter(PropertyInfo propertyInfo) => - (Func)CreatePropertyGetter(propertyInfo, typeof(TClass)); + public override Func CreateGetter(PropertyInfo propertyInfo) => + (Func)CreateGetter(typeof(TClass), propertyInfo); - private static Delegate CreatePropertyGetter(PropertyInfo propertyInfo, Type classType) + private static Delegate CreateGetter(Type classType, PropertyInfo propertyInfo) { MethodInfo? realMethod = propertyInfo.GetGetMethod(); - Type objectType = typeof(object); - Debug.Assert(realMethod != null); - var dynamicMethod = new DynamicMethod( - realMethod.Name, - propertyInfo.PropertyType, - new[] { objectType }, - typeof(ReflectionEmitMemberAccessor).Module, - skipVisibility: true); + DynamicMethod dynamicMethod = CreateGetterMethod(propertyInfo.Name, propertyInfo.PropertyType); ILGenerator generator = dynamicMethod.GetILGenerator(); generator.Emit(OpCodes.Ldarg_0); @@ -174,25 +167,48 @@ private static Delegate CreatePropertyGetter(PropertyInfo propertyInfo, Type cla generator.Emit(OpCodes.Ret); - return dynamicMethod.CreateDelegate(typeof(Func<,>).MakeGenericType(objectType, propertyInfo.PropertyType)); + return dynamicMethod + .CreateDelegate(typeof(Func<,>) + .MakeGenericType(typeof(object), propertyInfo.PropertyType)); } - public override Action CreatePropertySetter(PropertyInfo propertyInfo) => - (Action)CreatePropertySetter(propertyInfo, typeof(TClass)); + public override Func CreateGetter(FieldInfo fieldInfo) => + (Func)CreateGetter(typeof(TClass), fieldInfo); - private static Delegate CreatePropertySetter(PropertyInfo propertyInfo, Type classType) + private static Delegate CreateGetter(Type classType, FieldInfo fieldInfo) { - MethodInfo? realMethod = propertyInfo.GetSetMethod(); - Type objectType = typeof(object); + DynamicMethod dynamicMethod = CreateGetterMethod(fieldInfo.Name, fieldInfo.FieldType); + ILGenerator generator = dynamicMethod.GetILGenerator(); - Debug.Assert(realMethod != null); - var dynamicMethod = new DynamicMethod( - realMethod.Name, - typeof(void), - new[] { objectType, propertyInfo.PropertyType }, + generator.Emit(OpCodes.Ldarg_0); + generator.Emit( + classType.IsValueType + ? OpCodes.Unbox + : OpCodes.Castclass, + classType); + generator.Emit(OpCodes.Ldfld, fieldInfo); + generator.Emit(OpCodes.Ret); + + return dynamicMethod.CreateDelegate(typeof(Func<,>).MakeGenericType(typeof(object), fieldInfo.FieldType)); + } + + private static DynamicMethod CreateGetterMethod(string memberName, Type memberType) => + new DynamicMethod( + memberName + "Getter", + memberType, + new[] { typeof(object) }, typeof(ReflectionEmitMemberAccessor).Module, skipVisibility: true); + public override Action CreateSetter(PropertyInfo propertyInfo) => + (Action)CreateSetter(typeof(TClass), propertyInfo); + + private static Delegate CreateSetter(Type classType, PropertyInfo propertyInfo) + { + MethodInfo? realMethod = propertyInfo.GetSetMethod(); + Debug.Assert(realMethod != null); + + DynamicMethod dynamicMethod = CreateSetterMethod(propertyInfo.Name, propertyInfo.PropertyType); ILGenerator generator = dynamicMethod.GetILGenerator(); generator.Emit(OpCodes.Ldarg_0); @@ -212,8 +228,37 @@ private static Delegate CreatePropertySetter(PropertyInfo propertyInfo, Type cla generator.Emit(OpCodes.Ret); - return dynamicMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(objectType, propertyInfo.PropertyType)); + return dynamicMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeof(object), propertyInfo.PropertyType)); + } + + public override Action CreateSetter(FieldInfo fieldInfo) => + (Action)CreateSetter(typeof(TClass), fieldInfo); + + private static Delegate CreateSetter(Type classType, FieldInfo fieldInfo) + { + DynamicMethod dynamicMethod = CreateSetterMethod(fieldInfo.Name, fieldInfo.FieldType); + ILGenerator generator = dynamicMethod.GetILGenerator(); + + generator.Emit(OpCodes.Ldarg_0); + generator.Emit( + classType.IsValueType + ? OpCodes.Unbox + : OpCodes.Castclass, + classType); + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Stfld, fieldInfo); + generator.Emit(OpCodes.Ret); + + return dynamicMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeof(object), fieldInfo.FieldType)); } + + private static DynamicMethod CreateSetterMethod(string memberName, Type memberType) => + new DynamicMethod( + memberName + "Setter", + typeof(void), + new[] { typeof(object), memberType }, + typeof(ReflectionEmitMemberAccessor).Module, + skipVisibility: true); } } #endif diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMemberAccessor.cs index 0560db9adfe5ce..bb94cd97109748 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMemberAccessor.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMemberAccessor.cs @@ -24,6 +24,7 @@ internal sealed class ReflectionMemberAccessor : MemberAccessor private static readonly MethodInfo s_createStructPropertySetterMethod = new SetPropertyByRefFactory(CreateStructPropertySetter!) .Method.GetGenericMethodDefinition(); + public override JsonClassInfo.ConstructorDelegate? CreateConstructor(Type type) { Debug.Assert(type != null); @@ -92,7 +93,7 @@ public override ImmutableCollectionCreator ImmutableDictionaryCreateRange(Type c return creator; } - public override Func CreatePropertyGetter(PropertyInfo propertyInfo) + public override Func CreateGetter(PropertyInfo propertyInfo) { MethodInfo getMethodInfo = propertyInfo.GetGetMethod()!; @@ -113,7 +114,7 @@ public override ImmutableCollectionCreator ImmutableDictionaryCreateRange(Type c } } - public override Action CreatePropertySetter(PropertyInfo propertyInfo) + public override Action CreateSetter(PropertyInfo propertyInfo) { MethodInfo setMethodInfo = propertyInfo.GetSetMethod()!; @@ -149,13 +150,25 @@ private static Func CreateStructPropertyGetter CreateStructPropertySetter(SetPropertyByRef set) + private static Action CreateStructPropertySetter(SetPropertyByRef set) where TClass : struct { - return delegate (object obj, TProperty value) + return delegate (object? obj, TProperty value) { - set(ref Unsafe.Unbox(obj), value); + set(ref Unsafe.Unbox(obj!), value); }; } + + public override Func CreateGetter(FieldInfo fieldInfo) => + delegate (object? obj) + { + return (TProperty)fieldInfo.GetValue(obj)!; + }; + + public override Action CreateSetter(FieldInfo fieldInfo) => + delegate (object? obj, TProperty value) + { + fieldInfo.SetValue(obj, value); + }; } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs index 4e9bca3fa3c9c5..25de6a948fb152 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs @@ -95,7 +95,7 @@ public static void ThrowInvalidOperationException_SerializationConverterNotCompa [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowInvalidOperationException_SerializationConverterOnAttributeInvalid(Type classType, PropertyInfo? propertyInfo) + public static void ThrowInvalidOperationException_SerializationConverterOnAttributeInvalid(Type classType, MemberInfo? propertyInfo) { string location = classType.ToString(); if (propertyInfo != null) @@ -108,7 +108,7 @@ public static void ThrowInvalidOperationException_SerializationConverterOnAttrib [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowInvalidOperationException_SerializationConverterOnAttributeNotCompatible(Type classTypeAttributeIsOn, PropertyInfo? propertyInfo, Type typeToConvert) + public static void ThrowInvalidOperationException_SerializationConverterOnAttributeNotCompatible(Type classTypeAttributeIsOn, MemberInfo? propertyInfo, Type typeToConvert) { string location = classTypeAttributeIsOn.ToString(); @@ -242,7 +242,7 @@ public static void AddExceptionInformation(in WriteStack writeStack, JsonExcepti [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowInvalidOperationException_SerializationDuplicateAttribute(Type attribute, Type classType, PropertyInfo? propertyInfo) + public static void ThrowInvalidOperationException_SerializationDuplicateAttribute(Type attribute, Type classType, MemberInfo? propertyInfo) { string location = classType.ToString(); if (propertyInfo != null) diff --git a/src/libraries/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs b/src/libraries/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs index cc9f383f26c4cf..e0a24035cbbc72 100644 --- a/src/libraries/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs +++ b/src/libraries/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs @@ -12,6 +12,21 @@ namespace System.Text.Json.Serialization.Tests public static class PropertyVisibilityTests { [Fact] + + + + public static void ReadOnlyField() + { + var obj = new ClassWithReadOnlyField(); + + string json = JsonSerializer.Serialize(obj); + Assert.Contains(@"""MyString"":""DefaultValue""", json); + + obj = JsonSerializer.Deserialize(@"{""MyString"":""DeserializedValue""}"); + Assert.Equal("DefaultValue", obj.MyString); + } + + [Fact] public static void NoSetter() { var obj = new ClassWithNoSetter(); @@ -280,6 +295,11 @@ public class WrapperForClassWithIgnoredUnsupportedBigInteger public ClassWithIgnoredUnsupportedBigInteger MyClass { get; set; } } + public class ClassWithReadOnlyField + { + public readonly string MyString = "DefaultValue"; + } + // Todo: add tests with missing object property and missing collection property. public class ClassWithPrivateSetterAndGetter diff --git a/src/libraries/System.Text.Json/tests/Serialization/TestClasses.SimpleTestClassWithFields.cs b/src/libraries/System.Text.Json/tests/Serialization/TestClasses.SimpleTestClassWithFields.cs new file mode 100644 index 00000000000000..8458b67a0d2f93 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/Serialization/TestClasses.SimpleTestClassWithFields.cs @@ -0,0 +1,511 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Xunit; + +namespace System.Text.Json.Serialization.Tests +{ + public class SimpleTestClassWithFields : ITestClass + { + public short MyInt16; + public int MyInt32; + public long MyInt64; + public ushort MyUInt16; + public uint MyUInt32; + public ulong MyUInt64; + public byte MyByte; + public sbyte MySByte; + public char MyChar; + public string MyString; + public decimal MyDecimal; + public bool MyBooleanTrue; + public bool MyBooleanFalse; + public float MySingle; + public double MyDouble; + public DateTime MyDateTime; + public DateTimeOffset MyDateTimeOffset; + public Guid MyGuid; + public Uri MyUri; + public SampleEnumSByte MySByteEnum; + public SampleEnumByte MyByteEnum; + public SampleEnum MyEnum; + public SampleEnumInt16 MyInt16Enum; + public SampleEnumInt32 MyInt32Enum; + public SampleEnumInt64 MyInt64Enum; + public SampleEnumUInt16 MyUInt16Enum; + public SampleEnumUInt32 MyUInt32Enum; + public SampleEnumUInt64 MyUInt64Enum; + public SimpleStruct MySimpleStruct; + public SimpleTestStruct MySimpleTestStruct; + public short[] MyInt16Array; + public int[] MyInt32Array; + public long[] MyInt64Array; + public ushort[] MyUInt16Array; + public uint[] MyUInt32Array; + public ulong[] MyUInt64Array; + public byte[] MyByteArray; + public sbyte[] MySByteArray; + public char[] MyCharArray; + public string[] MyStringArray; + public decimal[] MyDecimalArray; + public bool[] MyBooleanTrueArray; + public bool[] MyBooleanFalseArray; + public float[] MySingleArray; + public double[] MyDoubleArray; + public DateTime[] MyDateTimeArray; + public DateTimeOffset[] MyDateTimeOffsetArray; + public Guid[] MyGuidArray; + public Uri[] MyUriArray; + public SampleEnum[] MyEnumArray; + public int[][] MyInt16TwoDimensionArray; + public List> MyInt16TwoDimensionList; + public int[][][] MyInt16ThreeDimensionArray; + public List>> MyInt16ThreeDimensionList; + public List MyStringList; + public IEnumerable MyStringIEnumerable; + public IList MyStringIList; + public ICollection MyStringICollection; + public IEnumerable MyStringIEnumerableT; + public IList MyStringIListT; + public ICollection MyStringICollectionT; + public IReadOnlyCollection MyStringIReadOnlyCollectionT; + public IReadOnlyList MyStringIReadOnlyListT; + public ISet MyStringISetT; + public KeyValuePair MyStringToStringKeyValuePair; + public IDictionary MyStringToStringIDict; + public Dictionary MyStringToStringGenericDict; + public IDictionary MyStringToStringGenericIDict; + public IReadOnlyDictionary MyStringToStringGenericIReadOnlyDict; + public ImmutableDictionary MyStringToStringImmutableDict; + public IImmutableDictionary MyStringToStringIImmutableDict; + public ImmutableSortedDictionary MyStringToStringImmutableSortedDict; + public Stack MyStringStackT; + public Queue MyStringQueueT; + public HashSet MyStringHashSetT; + public LinkedList MyStringLinkedListT; + public SortedSet MyStringSortedSetT; + public IImmutableList MyStringIImmutableListT; + public IImmutableStack MyStringIImmutableStackT; + public IImmutableQueue MyStringIImmutableQueueT; + public IImmutableSet MyStringIImmutableSetT; + public ImmutableHashSet MyStringImmutableHashSetT; + public ImmutableList MyStringImmutableListT; + public ImmutableStack MyStringImmutableStackT; + public ImmutableQueue MyStringImmutablQueueT; + public ImmutableSortedSet MyStringImmutableSortedSetT; + public List MyListOfNullString; + + public static readonly string s_json = $"{{{s_partialJsonProperties},{s_partialJsonArrays}}}"; + public static readonly string s_json_flipped = $"{{{s_partialJsonArrays},{s_partialJsonProperties}}}"; + + private const string s_partialJsonProperties = + @"""MyInt16"" : 1," + + @"""MyInt32"" : 2," + + @"""MyInt64"" : 3," + + @"""MyUInt16"" : 4," + + @"""MyUInt32"" : 5," + + @"""MyUInt64"" : 6," + + @"""MyByte"" : 7," + + @"""MySByte"" : 8," + + @"""MyChar"" : ""a""," + + @"""MyString"" : ""Hello""," + + @"""MyBooleanTrue"" : true," + + @"""MyBooleanFalse"" : false," + + @"""MySingle"" : 1.1," + + @"""MyDouble"" : 2.2," + + @"""MyDecimal"" : 3.3," + + @"""MyDateTime"" : ""2019-01-30T12:01:02.0000000Z""," + + @"""MyDateTimeOffset"" : ""2019-01-30T12:01:02.0000000+01:00""," + + @"""MyGuid"" : ""1B33498A-7B7D-4DDA-9C13-F6AA4AB449A6""," + + @"""MyUri"" : ""https:\/\/github.com\/dotnet\/corefx""," + + @"""MyEnum"" : 2," + // int by default + @"""MyInt64Enum"" : -9223372036854775808," + + @"""MyUInt64Enum"" : 18446744073709551615," + + @"""MyStringToStringKeyValuePair"" : {""Key"" : ""myKey"", ""Value"" : ""myValue""}," + + @"""MyStringToStringIDict"" : {""key"" : ""value""}," + + @"""MyStringToStringGenericDict"" : {""key"" : ""value""}," + + @"""MyStringToStringGenericIDict"" : {""key"" : ""value""}," + + @"""MyStringToStringGenericIReadOnlyDict"" : {""key"" : ""value""}," + + @"""MyStringToStringImmutableDict"" : {""key"" : ""value""}," + + @"""MyStringToStringIImmutableDict"" : {""key"" : ""value""}," + + @"""MyStringToStringImmutableSortedDict"" : {""key"" : ""value""}," + + @"""MySimpleStruct"" : {""One"" : 11, ""Two"" : 1.9999, ""Three"" : 33}," + + @"""MySimpleTestStruct"" : {""MyInt64"" : 64, ""MyString"" :""Hello"", ""MyInt32Array"" : [32]}"; + + private const string s_partialJsonArrays = + @"""MyInt16Array"" : [1]," + + @"""MyInt32Array"" : [2]," + + @"""MyInt64Array"" : [3]," + + @"""MyUInt16Array"" : [4]," + + @"""MyUInt32Array"" : [5]," + + @"""MyUInt64Array"" : [6]," + + @"""MyByteArray"" : ""Bw==""," + // Base64 encoded value of 7 + @"""MySByteArray"" : [8]," + + @"""MyCharArray"" : [""a""]," + + @"""MyStringArray"" : [""Hello""]," + + @"""MyBooleanTrueArray"" : [true]," + + @"""MyBooleanFalseArray"" : [false]," + + @"""MySingleArray"" : [1.1]," + + @"""MyDoubleArray"" : [2.2]," + + @"""MyDecimalArray"" : [3.3]," + + @"""MyDateTimeArray"" : [""2019-01-30T12:01:02.0000000Z""]," + + @"""MyDateTimeOffsetArray"" : [""2019-01-30T12:01:02.0000000+01:00""]," + + @"""MyGuidArray"" : [""1B33498A-7B7D-4DDA-9C13-F6AA4AB449A6""]," + + @"""MyUriArray"" : [""https:\/\/github.com\/dotnet\/corefx""]," + + @"""MyEnumArray"" : [2]," + // int by default + @"""MyInt16TwoDimensionArray"" : [[10, 11],[20, 21]]," + + @"""MyInt16TwoDimensionList"" : [[10, 11],[20, 21]]," + + @"""MyInt16ThreeDimensionArray"" : [[[11, 12],[13, 14]],[[21,22],[23,24]]]," + + @"""MyInt16ThreeDimensionList"" : [[[11, 12],[13, 14]],[[21,22],[23,24]]]," + + @"""MyStringList"" : [""Hello""]," + + @"""MyStringIEnumerable"" : [""Hello""]," + + @"""MyStringIList"" : [""Hello""]," + + @"""MyStringICollection"" : [""Hello""]," + + @"""MyStringIEnumerableT"" : [""Hello""]," + + @"""MyStringIListT"" : [""Hello""]," + + @"""MyStringICollectionT"" : [""Hello""]," + + @"""MyStringIReadOnlyCollectionT"" : [""Hello""]," + + @"""MyStringIReadOnlyListT"" : [""Hello""]," + + @"""MyStringISetT"" : [""Hello""]," + + @"""MyStringStackT"" : [""Hello"", ""World""]," + + @"""MyStringQueueT"" : [""Hello"", ""World""]," + + @"""MyStringHashSetT"" : [""Hello""]," + + @"""MyStringLinkedListT"" : [""Hello""]," + + @"""MyStringSortedSetT"" : [""Hello""]," + + @"""MyStringIImmutableListT"" : [""Hello""]," + + @"""MyStringIImmutableStackT"" : [""Hello""]," + + @"""MyStringIImmutableQueueT"" : [""Hello""]," + + @"""MyStringIImmutableSetT"" : [""Hello""]," + + @"""MyStringImmutableHashSetT"" : [""Hello""]," + + @"""MyStringImmutableListT"" : [""Hello""]," + + @"""MyStringImmutableStackT"" : [""Hello""]," + + @"""MyStringImmutablQueueT"" : [""Hello""]," + + @"""MyStringImmutableSortedSetT"" : [""Hello""]," + + @"""MyListOfNullString"" : [null]"; + + public static readonly byte[] s_data = Encoding.UTF8.GetBytes(s_json); + + public void Initialize() + { + MyInt16 = 1; + MyInt32 = 2; + MyInt64 = 3; + MyUInt16 = 4; + MyUInt32 = 5; + MyUInt64 = 6; + MyByte = 7; + MySByte = 8; + MyChar = 'a'; + MyString = "Hello"; + MyBooleanTrue = true; + MyBooleanFalse = false; + MySingle = 1.1f; + MyDouble = 2.2d; + MyDecimal = 3.3m; + MyDateTime = new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc); + MyDateTimeOffset = new DateTimeOffset(2019, 1, 30, 12, 1, 2, new TimeSpan(1, 0, 0)); + MyGuid = new Guid("1B33498A-7B7D-4DDA-9C13-F6AA4AB449A6"); + MyUri = new Uri("https://github.com/dotnet/corefx"); + MyEnum = SampleEnum.Two; + MyInt64Enum = SampleEnumInt64.MinNegative; + MyUInt64Enum = SampleEnumUInt64.Max; + MyInt16Array = new short[] { 1 }; + MyInt32Array = new int[] { 2 }; + MyInt64Array = new long[] { 3 }; + MyUInt16Array = new ushort[] { 4 }; + MyUInt32Array = new uint[] { 5 }; + MyUInt64Array = new ulong[] { 6 }; + MyByteArray = new byte[] { 7 }; + MySByteArray = new sbyte[] { 8 }; + MyCharArray = new char[] { 'a' }; + MyStringArray = new string[] { "Hello" }; + MyBooleanTrueArray = new bool[] { true }; + MyBooleanFalseArray = new bool[] { false }; + MySingleArray = new float[] { 1.1f }; + MyDoubleArray = new double[] { 2.2d }; + MyDecimalArray = new decimal[] { 3.3m }; + MyDateTimeArray = new DateTime[] { new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc) }; + MyDateTimeOffsetArray = new DateTimeOffset[] { new DateTimeOffset(2019, 1, 30, 12, 1, 2, new TimeSpan(1, 0, 0)) }; + MyGuidArray = new Guid[] { new Guid("1B33498A-7B7D-4DDA-9C13-F6AA4AB449A6") }; + MyUriArray = new Uri[] { new Uri("https://github.com/dotnet/corefx") }; + MyEnumArray = new SampleEnum[] { SampleEnum.Two }; + MySimpleStruct = new SimpleStruct { One = 11, Two = 1.9999 }; + MySimpleTestStruct = new SimpleTestStruct { MyInt64 = 64, MyString = "Hello", MyInt32Array = new int[] { 32 } }; + + MyInt16TwoDimensionArray = new int[2][]; + MyInt16TwoDimensionArray[0] = new int[] { 10, 11 }; + MyInt16TwoDimensionArray[1] = new int[] { 20, 21 }; + + MyInt16TwoDimensionList = new List>(); + MyInt16TwoDimensionList.Add(new List { 10, 11 }); + MyInt16TwoDimensionList.Add(new List { 20, 21 }); + + MyInt16ThreeDimensionArray = new int[2][][]; + MyInt16ThreeDimensionArray[0] = new int[2][]; + MyInt16ThreeDimensionArray[1] = new int[2][]; + MyInt16ThreeDimensionArray[0][0] = new int[] { 11, 12 }; + MyInt16ThreeDimensionArray[0][1] = new int[] { 13, 14 }; + MyInt16ThreeDimensionArray[1][0] = new int[] { 21, 22 }; + MyInt16ThreeDimensionArray[1][1] = new int[] { 23, 24 }; + + MyInt16ThreeDimensionList = new List>>(); + var list1 = new List>(); + MyInt16ThreeDimensionList.Add(list1); + list1.Add(new List { 11, 12 }); + list1.Add(new List { 13, 14 }); + var list2 = new List>(); + MyInt16ThreeDimensionList.Add(list2); + list2.Add(new List { 21, 22 }); + list2.Add(new List { 23, 24 }); + + MyStringList = new List() { "Hello" }; + + MyStringIEnumerable = new string[] { "Hello" }; + MyStringIList = new string[] { "Hello" }; + MyStringICollection = new string[] { "Hello" }; + + MyStringIEnumerableT = new string[] { "Hello" }; + MyStringIListT = new string[] { "Hello" }; + MyStringICollectionT = new string[] { "Hello" }; + MyStringIReadOnlyCollectionT = new string[] { "Hello" }; + MyStringIReadOnlyListT = new string[] { "Hello" }; + MyStringISetT = new HashSet { "Hello" }; + + MyStringToStringKeyValuePair = new KeyValuePair("myKey", "myValue"); + MyStringToStringIDict = new Dictionary { { "key", "value" } }; + + MyStringToStringGenericDict = new Dictionary { { "key", "value" } }; + MyStringToStringGenericIDict = new Dictionary { { "key", "value" } }; + MyStringToStringGenericIReadOnlyDict = new Dictionary { { "key", "value" } }; + + MyStringToStringImmutableDict = ImmutableDictionary.CreateRange(MyStringToStringGenericDict); + MyStringToStringIImmutableDict = ImmutableDictionary.CreateRange(MyStringToStringGenericDict); + MyStringToStringImmutableSortedDict = ImmutableSortedDictionary.CreateRange(MyStringToStringGenericDict); + + MyStringStackT = new Stack(new List() { "Hello", "World" }); + MyStringQueueT = new Queue(new List() { "Hello", "World" }); + MyStringHashSetT = new HashSet(new List() { "Hello" }); + MyStringLinkedListT = new LinkedList(new List() { "Hello" }); + MyStringSortedSetT = new SortedSet(new List() { "Hello" }); + + MyStringIImmutableListT = ImmutableList.CreateRange(new List { "Hello" }); + MyStringIImmutableStackT = ImmutableStack.CreateRange(new List { "Hello" }); + MyStringIImmutableQueueT = ImmutableQueue.CreateRange(new List { "Hello" }); + MyStringIImmutableSetT = ImmutableHashSet.CreateRange(new List { "Hello" }); + MyStringImmutableHashSetT = ImmutableHashSet.CreateRange(new List { "Hello" }); + MyStringImmutableListT = ImmutableList.CreateRange(new List { "Hello" }); + MyStringImmutableStackT = ImmutableStack.CreateRange(new List { "Hello" }); + MyStringImmutablQueueT = ImmutableQueue.CreateRange(new List { "Hello" }); + MyStringImmutableSortedSetT = ImmutableSortedSet.CreateRange(new List { "Hello" }); + + MyListOfNullString = new List { null }; + } + + public void Verify() + { + Assert.Equal((short)1, MyInt16); + Assert.Equal((int)2, MyInt32); + Assert.Equal((long)3, MyInt64); + Assert.Equal((ushort)4, MyUInt16); + Assert.Equal((uint)5, MyUInt32); + Assert.Equal((ulong)6, MyUInt64); + Assert.Equal((byte)7, MyByte); + Assert.Equal((sbyte)8, MySByte); + Assert.Equal('a', MyChar); + Assert.Equal("Hello", MyString); + Assert.Equal(3.3m, MyDecimal); + Assert.False(MyBooleanFalse); + Assert.True(MyBooleanTrue); + Assert.Equal(1.1f, MySingle); + Assert.Equal(2.2d, MyDouble); + Assert.Equal(new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc), MyDateTime); + Assert.Equal(new DateTimeOffset(2019, 1, 30, 12, 1, 2, new TimeSpan(1, 0, 0)), MyDateTimeOffset); + Assert.Equal(new Guid("1B33498A-7B7D-4DDA-9C13-F6AA4AB449A6"), MyGuid); + Assert.Equal(new Uri("https://github.com/dotnet/corefx"), MyUri); + Assert.Equal(SampleEnum.Two, MyEnum); + Assert.Equal(SampleEnumInt64.MinNegative, MyInt64Enum); + Assert.Equal(SampleEnumUInt64.Max, MyUInt64Enum); + Assert.Equal(11, MySimpleStruct.One); + Assert.Equal(1.9999, MySimpleStruct.Two); + Assert.Equal(64, MySimpleTestStruct.MyInt64); + Assert.Equal("Hello", MySimpleTestStruct.MyString); + Assert.Equal(32, MySimpleTestStruct.MyInt32Array[0]); + + Assert.Equal((short)1, MyInt16Array[0]); + Assert.Equal((int)2, MyInt32Array[0]); + Assert.Equal((long)3, MyInt64Array[0]); + Assert.Equal((ushort)4, MyUInt16Array[0]); + Assert.Equal((uint)5, MyUInt32Array[0]); + Assert.Equal((ulong)6, MyUInt64Array[0]); + Assert.Equal((byte)7, MyByteArray[0]); + Assert.Equal((sbyte)8, MySByteArray[0]); + Assert.Equal('a', MyCharArray[0]); + Assert.Equal("Hello", MyStringArray[0]); + Assert.Equal(3.3m, MyDecimalArray[0]); + Assert.False(MyBooleanFalseArray[0]); + Assert.True(MyBooleanTrueArray[0]); + Assert.Equal(1.1f, MySingleArray[0]); + Assert.Equal(2.2d, MyDoubleArray[0]); + Assert.Equal(new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc), MyDateTimeArray[0]); + Assert.Equal(new DateTimeOffset(2019, 1, 30, 12, 1, 2, new TimeSpan(1, 0, 0)), MyDateTimeOffsetArray[0]); + Assert.Equal(new Guid("1B33498A-7B7D-4DDA-9C13-F6AA4AB449A6"), MyGuidArray[0]); + Assert.Equal(new Uri("https://github.com/dotnet/corefx"), MyUriArray[0]); + Assert.Equal(SampleEnum.Two, MyEnumArray[0]); + + Assert.Equal(10, MyInt16TwoDimensionArray[0][0]); + Assert.Equal(11, MyInt16TwoDimensionArray[0][1]); + Assert.Equal(20, MyInt16TwoDimensionArray[1][0]); + Assert.Equal(21, MyInt16TwoDimensionArray[1][1]); + + Assert.Equal(10, MyInt16TwoDimensionList[0][0]); + Assert.Equal(11, MyInt16TwoDimensionList[0][1]); + Assert.Equal(20, MyInt16TwoDimensionList[1][0]); + Assert.Equal(21, MyInt16TwoDimensionList[1][1]); + + Assert.Equal(11, MyInt16ThreeDimensionArray[0][0][0]); + Assert.Equal(12, MyInt16ThreeDimensionArray[0][0][1]); + Assert.Equal(13, MyInt16ThreeDimensionArray[0][1][0]); + Assert.Equal(14, MyInt16ThreeDimensionArray[0][1][1]); + Assert.Equal(21, MyInt16ThreeDimensionArray[1][0][0]); + Assert.Equal(22, MyInt16ThreeDimensionArray[1][0][1]); + Assert.Equal(23, MyInt16ThreeDimensionArray[1][1][0]); + Assert.Equal(24, MyInt16ThreeDimensionArray[1][1][1]); + + Assert.Equal(11, MyInt16ThreeDimensionList[0][0][0]); + Assert.Equal(12, MyInt16ThreeDimensionList[0][0][1]); + Assert.Equal(13, MyInt16ThreeDimensionList[0][1][0]); + Assert.Equal(14, MyInt16ThreeDimensionList[0][1][1]); + Assert.Equal(21, MyInt16ThreeDimensionList[1][0][0]); + Assert.Equal(22, MyInt16ThreeDimensionList[1][0][1]); + Assert.Equal(23, MyInt16ThreeDimensionList[1][1][0]); + Assert.Equal(24, MyInt16ThreeDimensionList[1][1][1]); + + Assert.Equal("Hello", MyStringList[0]); + + IEnumerator enumerator = MyStringIEnumerable.GetEnumerator(); + enumerator.MoveNext(); + { + // Verifying after deserialization. + if (enumerator.Current is JsonElement currentJsonElement) + { + Assert.Equal("Hello", currentJsonElement.GetString()); + } + // Verifying test data. + else + { + Assert.Equal("Hello", enumerator.Current); + } + } + + { + // Verifying after deserialization. + if (MyStringIList[0] is JsonElement currentJsonElement) + { + Assert.Equal("Hello", currentJsonElement.GetString()); + } + // Verifying test data. + else + { + Assert.Equal("Hello", enumerator.Current); + } + } + + enumerator = MyStringICollection.GetEnumerator(); + enumerator.MoveNext(); + { + // Verifying after deserialization. + if (enumerator.Current is JsonElement currentJsonElement) + { + Assert.Equal("Hello", currentJsonElement.GetString()); + } + // Verifying test data. + else + { + Assert.Equal("Hello", enumerator.Current); + } + } + + Assert.Equal("Hello", MyStringIEnumerableT.First()); + Assert.Equal("Hello", MyStringIListT[0]); + Assert.Equal("Hello", MyStringICollectionT.First()); + Assert.Equal("Hello", MyStringIReadOnlyCollectionT.First()); + Assert.Equal("Hello", MyStringIReadOnlyListT[0]); + Assert.Equal("Hello", MyStringISetT.First()); + + enumerator = MyStringToStringIDict.GetEnumerator(); + enumerator.MoveNext(); + { + // Verifying after deserialization. + if (enumerator.Current is JsonElement currentJsonElement) + { + IEnumerator jsonEnumerator = currentJsonElement.EnumerateObject(); + jsonEnumerator.MoveNext(); + + JsonProperty property = (JsonProperty)jsonEnumerator.Current; + + Assert.Equal("key", property.Name); + Assert.Equal("value", property.Value.GetString()); + } + // Verifying test data. + else + { + DictionaryEntry entry = (DictionaryEntry)enumerator.Current; + Assert.Equal("key", entry.Key); + + if (entry.Value is JsonElement element) + { + Assert.Equal("value", element.GetString()); + } + else + { + Assert.Equal("value", entry.Value); + } + } + } + + Assert.Equal("value", MyStringToStringGenericDict["key"]); + Assert.Equal("value", MyStringToStringGenericIDict["key"]); + Assert.Equal("value", MyStringToStringGenericIReadOnlyDict["key"]); + + Assert.Equal("value", MyStringToStringImmutableDict["key"]); + Assert.Equal("value", MyStringToStringIImmutableDict["key"]); + Assert.Equal("value", MyStringToStringImmutableSortedDict["key"]); + + Assert.Equal("myKey", MyStringToStringKeyValuePair.Key); + Assert.Equal("myValue", MyStringToStringKeyValuePair.Value); + + Assert.Equal(2, MyStringStackT.Count); + Assert.True(MyStringStackT.Contains("Hello")); + Assert.True(MyStringStackT.Contains("World")); + + string[] expectedQueue = { "Hello", "World" }; + int i = 0; + foreach (string item in MyStringQueueT) + { + Assert.Equal(expectedQueue[i], item); + i++; + } + + Assert.Equal("Hello", MyStringHashSetT.First()); + Assert.Equal("Hello", MyStringLinkedListT.First()); + Assert.Equal("Hello", MyStringSortedSetT.First()); + + Assert.Equal("Hello", MyStringIImmutableListT[0]); + Assert.Equal("Hello", MyStringIImmutableStackT.First()); + Assert.Equal("Hello", MyStringIImmutableQueueT.First()); + Assert.Equal("Hello", MyStringIImmutableSetT.First()); + Assert.Equal("Hello", MyStringImmutableHashSetT.First()); + Assert.Equal("Hello", MyStringImmutableListT[0]); + Assert.Equal("Hello", MyStringImmutableStackT.First()); + Assert.Equal("Hello", MyStringImmutablQueueT.First()); + Assert.Equal("Hello", MyStringImmutableSortedSetT.First()); + + Assert.Null(MyListOfNullString[0]); + } + } +} diff --git a/src/libraries/System.Text.Json/tests/Serialization/TestClasses.SimpleTestStructWithFields.cs b/src/libraries/System.Text.Json/tests/Serialization/TestClasses.SimpleTestStructWithFields.cs new file mode 100644 index 00000000000000..a0f28f39b61142 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/Serialization/TestClasses.SimpleTestStructWithFields.cs @@ -0,0 +1,511 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Xunit; + +namespace System.Text.Json.Serialization.Tests +{ + public struct SimpleTestStructWithFields : ITestClass + { + public short MyInt16; + public int MyInt32; + public long MyInt64; + public ushort MyUInt16; + public uint MyUInt32; + public ulong MyUInt64; + public byte MyByte; + public sbyte MySByte; + public char MyChar; + public string MyString; + public decimal MyDecimal; + public bool MyBooleanTrue; + public bool MyBooleanFalse; + public float MySingle; + public double MyDouble; + public DateTime MyDateTime; + public DateTimeOffset MyDateTimeOffset; + public Guid MyGuid; + public Uri MyUri; + public SampleEnumSByte MySByteEnum; + public SampleEnumByte MyByteEnum; + public SampleEnum MyEnum; + public SampleEnumInt16 MyInt16Enum; + public SampleEnumInt32 MyInt32Enum; + public SampleEnumInt64 MyInt64Enum; + public SampleEnumUInt16 MyUInt16Enum; + public SampleEnumUInt32 MyUInt32Enum; + public SampleEnumUInt64 MyUInt64Enum; + public SimpleStruct MySimpleStruct; + public SimpleTestStruct MySimpleTestStruct; + public short[] MyInt16Array; + public int[] MyInt32Array; + public long[] MyInt64Array; + public ushort[] MyUInt16Array; + public uint[] MyUInt32Array; + public ulong[] MyUInt64Array; + public byte[] MyByteArray; + public sbyte[] MySByteArray; + public char[] MyCharArray; + public string[] MyStringArray; + public decimal[] MyDecimalArray; + public bool[] MyBooleanTrueArray; + public bool[] MyBooleanFalseArray; + public float[] MySingleArray; + public double[] MyDoubleArray; + public DateTime[] MyDateTimeArray; + public DateTimeOffset[] MyDateTimeOffsetArray; + public Guid[] MyGuidArray; + public Uri[] MyUriArray; + public SampleEnum[] MyEnumArray; + public int[][] MyInt16TwoDimensionArray; + public List> MyInt16TwoDimensionList; + public int[][][] MyInt16ThreeDimensionArray; + public List>> MyInt16ThreeDimensionList; + public List MyStringList; + public IEnumerable MyStringIEnumerable; + public IList MyStringIList; + public ICollection MyStringICollection; + public IEnumerable MyStringIEnumerableT; + public IList MyStringIListT; + public ICollection MyStringICollectionT; + public IReadOnlyCollection MyStringIReadOnlyCollectionT; + public IReadOnlyList MyStringIReadOnlyListT; + public ISet MyStringISetT; + public KeyValuePair MyStringToStringKeyValuePair; + public IDictionary MyStringToStringIDict; + public Dictionary MyStringToStringGenericDict; + public IDictionary MyStringToStringGenericIDict; + public IReadOnlyDictionary MyStringToStringGenericIReadOnlyDict; + public ImmutableDictionary MyStringToStringImmutableDict; + public IImmutableDictionary MyStringToStringIImmutableDict; + public ImmutableSortedDictionary MyStringToStringImmutableSortedDict; + public Stack MyStringStackT; + public Queue MyStringQueueT; + public HashSet MyStringHashSetT; + public LinkedList MyStringLinkedListT; + public SortedSet MyStringSortedSetT; + public IImmutableList MyStringIImmutableListT; + public IImmutableStack MyStringIImmutableStackT; + public IImmutableQueue MyStringIImmutableQueueT; + public IImmutableSet MyStringIImmutableSetT; + public ImmutableHashSet MyStringImmutableHashSetT; + public ImmutableList MyStringImmutableListT; + public ImmutableStack MyStringImmutableStackT; + public ImmutableQueue MyStringImmutablQueueT; + public ImmutableSortedSet MyStringImmutableSortedSetT; + public List MyListOfNullString; + + public static readonly string s_json = $"{{{s_partialJsonProperties},{s_partialJsonArrays}}}"; + public static readonly string s_json_flipped = $"{{{s_partialJsonArrays},{s_partialJsonProperties}}}"; + + private const string s_partialJsonProperties = + @"""MyInt16"" : 1," + + @"""MyInt32"" : 2," + + @"""MyInt64"" : 3," + + @"""MyUInt16"" : 4," + + @"""MyUInt32"" : 5," + + @"""MyUInt64"" : 6," + + @"""MyByte"" : 7," + + @"""MySByte"" : 8," + + @"""MyChar"" : ""a""," + + @"""MyString"" : ""Hello""," + + @"""MyBooleanTrue"" : true," + + @"""MyBooleanFalse"" : false," + + @"""MySingle"" : 1.1," + + @"""MyDouble"" : 2.2," + + @"""MyDecimal"" : 3.3," + + @"""MyDateTime"" : ""2019-01-30T12:01:02.0000000Z""," + + @"""MyDateTimeOffset"" : ""2019-01-30T12:01:02.0000000+01:00""," + + @"""MyGuid"" : ""1B33498A-7B7D-4DDA-9C13-F6AA4AB449A6""," + + @"""MyUri"" : ""https:\/\/github.com\/dotnet\/corefx""," + + @"""MyEnum"" : 2," + // int by default + @"""MyInt64Enum"" : -9223372036854775808," + + @"""MyUInt64Enum"" : 18446744073709551615," + + @"""MyStringToStringKeyValuePair"" : {""Key"" : ""myKey"", ""Value"" : ""myValue""}," + + @"""MyStringToStringIDict"" : {""key"" : ""value""}," + + @"""MyStringToStringGenericDict"" : {""key"" : ""value""}," + + @"""MyStringToStringGenericIDict"" : {""key"" : ""value""}," + + @"""MyStringToStringGenericIReadOnlyDict"" : {""key"" : ""value""}," + + @"""MyStringToStringImmutableDict"" : {""key"" : ""value""}," + + @"""MyStringToStringIImmutableDict"" : {""key"" : ""value""}," + + @"""MyStringToStringImmutableSortedDict"" : {""key"" : ""value""}," + + @"""MySimpleStruct"" : {""One"" : 11, ""Two"" : 1.9999, ""Three"" : 33}," + + @"""MySimpleTestStruct"" : {""MyInt64"" : 64, ""MyString"" :""Hello"", ""MyInt32Array"" : [32]}"; + + private const string s_partialJsonArrays = + @"""MyInt16Array"" : [1]," + + @"""MyInt32Array"" : [2]," + + @"""MyInt64Array"" : [3]," + + @"""MyUInt16Array"" : [4]," + + @"""MyUInt32Array"" : [5]," + + @"""MyUInt64Array"" : [6]," + + @"""MyByteArray"" : ""Bw==""," + // Base64 encoded value of 7 + @"""MySByteArray"" : [8]," + + @"""MyCharArray"" : [""a""]," + + @"""MyStringArray"" : [""Hello""]," + + @"""MyBooleanTrueArray"" : [true]," + + @"""MyBooleanFalseArray"" : [false]," + + @"""MySingleArray"" : [1.1]," + + @"""MyDoubleArray"" : [2.2]," + + @"""MyDecimalArray"" : [3.3]," + + @"""MyDateTimeArray"" : [""2019-01-30T12:01:02.0000000Z""]," + + @"""MyDateTimeOffsetArray"" : [""2019-01-30T12:01:02.0000000+01:00""]," + + @"""MyGuidArray"" : [""1B33498A-7B7D-4DDA-9C13-F6AA4AB449A6""]," + + @"""MyUriArray"" : [""https:\/\/github.com\/dotnet\/corefx""]," + + @"""MyEnumArray"" : [2]," + // int by default + @"""MyInt16TwoDimensionArray"" : [[10, 11],[20, 21]]," + + @"""MyInt16TwoDimensionList"" : [[10, 11],[20, 21]]," + + @"""MyInt16ThreeDimensionArray"" : [[[11, 12],[13, 14]],[[21,22],[23,24]]]," + + @"""MyInt16ThreeDimensionList"" : [[[11, 12],[13, 14]],[[21,22],[23,24]]]," + + @"""MyStringList"" : [""Hello""]," + + @"""MyStringIEnumerable"" : [""Hello""]," + + @"""MyStringIList"" : [""Hello""]," + + @"""MyStringICollection"" : [""Hello""]," + + @"""MyStringIEnumerableT"" : [""Hello""]," + + @"""MyStringIListT"" : [""Hello""]," + + @"""MyStringICollectionT"" : [""Hello""]," + + @"""MyStringIReadOnlyCollectionT"" : [""Hello""]," + + @"""MyStringIReadOnlyListT"" : [""Hello""]," + + @"""MyStringISetT"" : [""Hello""]," + + @"""MyStringStackT"" : [""Hello"", ""World""]," + + @"""MyStringQueueT"" : [""Hello"", ""World""]," + + @"""MyStringHashSetT"" : [""Hello""]," + + @"""MyStringLinkedListT"" : [""Hello""]," + + @"""MyStringSortedSetT"" : [""Hello""]," + + @"""MyStringIImmutableListT"" : [""Hello""]," + + @"""MyStringIImmutableStackT"" : [""Hello""]," + + @"""MyStringIImmutableQueueT"" : [""Hello""]," + + @"""MyStringIImmutableSetT"" : [""Hello""]," + + @"""MyStringImmutableHashSetT"" : [""Hello""]," + + @"""MyStringImmutableListT"" : [""Hello""]," + + @"""MyStringImmutableStackT"" : [""Hello""]," + + @"""MyStringImmutablQueueT"" : [""Hello""]," + + @"""MyStringImmutableSortedSetT"" : [""Hello""]," + + @"""MyListOfNullString"" : [null]"; + + public static readonly byte[] s_data = Encoding.UTF8.GetBytes(s_json); + + public void Initialize() + { + MyInt16 = 1; + MyInt32 = 2; + MyInt64 = 3; + MyUInt16 = 4; + MyUInt32 = 5; + MyUInt64 = 6; + MyByte = 7; + MySByte = 8; + MyChar = 'a'; + MyString = "Hello"; + MyBooleanTrue = true; + MyBooleanFalse = false; + MySingle = 1.1f; + MyDouble = 2.2d; + MyDecimal = 3.3m; + MyDateTime = new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc); + MyDateTimeOffset = new DateTimeOffset(2019, 1, 30, 12, 1, 2, new TimeSpan(1, 0, 0)); + MyGuid = new Guid("1B33498A-7B7D-4DDA-9C13-F6AA4AB449A6"); + MyUri = new Uri("https://github.com/dotnet/corefx"); + MyEnum = SampleEnum.Two; + MyInt64Enum = SampleEnumInt64.MinNegative; + MyUInt64Enum = SampleEnumUInt64.Max; + MyInt16Array = new short[] { 1 }; + MyInt32Array = new int[] { 2 }; + MyInt64Array = new long[] { 3 }; + MyUInt16Array = new ushort[] { 4 }; + MyUInt32Array = new uint[] { 5 }; + MyUInt64Array = new ulong[] { 6 }; + MyByteArray = new byte[] { 7 }; + MySByteArray = new sbyte[] { 8 }; + MyCharArray = new char[] { 'a' }; + MyStringArray = new string[] { "Hello" }; + MyBooleanTrueArray = new bool[] { true }; + MyBooleanFalseArray = new bool[] { false }; + MySingleArray = new float[] { 1.1f }; + MyDoubleArray = new double[] { 2.2d }; + MyDecimalArray = new decimal[] { 3.3m }; + MyDateTimeArray = new DateTime[] { new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc) }; + MyDateTimeOffsetArray = new DateTimeOffset[] { new DateTimeOffset(2019, 1, 30, 12, 1, 2, new TimeSpan(1, 0, 0)) }; + MyGuidArray = new Guid[] { new Guid("1B33498A-7B7D-4DDA-9C13-F6AA4AB449A6") }; + MyUriArray = new Uri[] { new Uri("https://github.com/dotnet/corefx") }; + MyEnumArray = new SampleEnum[] { SampleEnum.Two }; + MySimpleStruct = new SimpleStruct { One = 11, Two = 1.9999 }; + MySimpleTestStruct = new SimpleTestStruct { MyInt64 = 64, MyString = "Hello", MyInt32Array = new int[] { 32 } }; + + MyInt16TwoDimensionArray = new int[2][]; + MyInt16TwoDimensionArray[0] = new int[] { 10, 11 }; + MyInt16TwoDimensionArray[1] = new int[] { 20, 21 }; + + MyInt16TwoDimensionList = new List>(); + MyInt16TwoDimensionList.Add(new List { 10, 11 }); + MyInt16TwoDimensionList.Add(new List { 20, 21 }); + + MyInt16ThreeDimensionArray = new int[2][][]; + MyInt16ThreeDimensionArray[0] = new int[2][]; + MyInt16ThreeDimensionArray[1] = new int[2][]; + MyInt16ThreeDimensionArray[0][0] = new int[] { 11, 12 }; + MyInt16ThreeDimensionArray[0][1] = new int[] { 13, 14 }; + MyInt16ThreeDimensionArray[1][0] = new int[] { 21, 22 }; + MyInt16ThreeDimensionArray[1][1] = new int[] { 23, 24 }; + + MyInt16ThreeDimensionList = new List>>(); + var list1 = new List>(); + MyInt16ThreeDimensionList.Add(list1); + list1.Add(new List { 11, 12 }); + list1.Add(new List { 13, 14 }); + var list2 = new List>(); + MyInt16ThreeDimensionList.Add(list2); + list2.Add(new List { 21, 22 }); + list2.Add(new List { 23, 24 }); + + MyStringList = new List() { "Hello" }; + + MyStringIEnumerable = new string[] { "Hello" }; + MyStringIList = new string[] { "Hello" }; + MyStringICollection = new string[] { "Hello" }; + + MyStringIEnumerableT = new string[] { "Hello" }; + MyStringIListT = new string[] { "Hello" }; + MyStringICollectionT = new string[] { "Hello" }; + MyStringIReadOnlyCollectionT = new string[] { "Hello" }; + MyStringIReadOnlyListT = new string[] { "Hello" }; + MyStringISetT = new HashSet { "Hello" }; + + MyStringToStringKeyValuePair = new KeyValuePair("myKey", "myValue"); + MyStringToStringIDict = new Dictionary { { "key", "value" } }; + + MyStringToStringGenericDict = new Dictionary { { "key", "value" } }; + MyStringToStringGenericIDict = new Dictionary { { "key", "value" } }; + MyStringToStringGenericIReadOnlyDict = new Dictionary { { "key", "value" } }; + + MyStringToStringImmutableDict = ImmutableDictionary.CreateRange(MyStringToStringGenericDict); + MyStringToStringIImmutableDict = ImmutableDictionary.CreateRange(MyStringToStringGenericDict); + MyStringToStringImmutableSortedDict = ImmutableSortedDictionary.CreateRange(MyStringToStringGenericDict); + + MyStringStackT = new Stack(new List() { "Hello", "World" }); + MyStringQueueT = new Queue(new List() { "Hello", "World" }); + MyStringHashSetT = new HashSet(new List() { "Hello" }); + MyStringLinkedListT = new LinkedList(new List() { "Hello" }); + MyStringSortedSetT = new SortedSet(new List() { "Hello" }); + + MyStringIImmutableListT = ImmutableList.CreateRange(new List { "Hello" }); + MyStringIImmutableStackT = ImmutableStack.CreateRange(new List { "Hello" }); + MyStringIImmutableQueueT = ImmutableQueue.CreateRange(new List { "Hello" }); + MyStringIImmutableSetT = ImmutableHashSet.CreateRange(new List { "Hello" }); + MyStringImmutableHashSetT = ImmutableHashSet.CreateRange(new List { "Hello" }); + MyStringImmutableListT = ImmutableList.CreateRange(new List { "Hello" }); + MyStringImmutableStackT = ImmutableStack.CreateRange(new List { "Hello" }); + MyStringImmutablQueueT = ImmutableQueue.CreateRange(new List { "Hello" }); + MyStringImmutableSortedSetT = ImmutableSortedSet.CreateRange(new List { "Hello" }); + + MyListOfNullString = new List { null }; + } + + public void Verify() + { + Assert.Equal((short)1, MyInt16); + Assert.Equal((int)2, MyInt32); + Assert.Equal((long)3, MyInt64); + Assert.Equal((ushort)4, MyUInt16); + Assert.Equal((uint)5, MyUInt32); + Assert.Equal((ulong)6, MyUInt64); + Assert.Equal((byte)7, MyByte); + Assert.Equal((sbyte)8, MySByte); + Assert.Equal('a', MyChar); + Assert.Equal("Hello", MyString); + Assert.Equal(3.3m, MyDecimal); + Assert.False(MyBooleanFalse); + Assert.True(MyBooleanTrue); + Assert.Equal(1.1f, MySingle); + Assert.Equal(2.2d, MyDouble); + Assert.Equal(new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc), MyDateTime); + Assert.Equal(new DateTimeOffset(2019, 1, 30, 12, 1, 2, new TimeSpan(1, 0, 0)), MyDateTimeOffset); + Assert.Equal(new Guid("1B33498A-7B7D-4DDA-9C13-F6AA4AB449A6"), MyGuid); + Assert.Equal(new Uri("https://github.com/dotnet/corefx"), MyUri); + Assert.Equal(SampleEnum.Two, MyEnum); + Assert.Equal(SampleEnumInt64.MinNegative, MyInt64Enum); + Assert.Equal(SampleEnumUInt64.Max, MyUInt64Enum); + Assert.Equal(11, MySimpleStruct.One); + Assert.Equal(1.9999, MySimpleStruct.Two); + Assert.Equal(64, MySimpleTestStruct.MyInt64); + Assert.Equal("Hello", MySimpleTestStruct.MyString); + Assert.Equal(32, MySimpleTestStruct.MyInt32Array[0]); + + Assert.Equal((short)1, MyInt16Array[0]); + Assert.Equal((int)2, MyInt32Array[0]); + Assert.Equal((long)3, MyInt64Array[0]); + Assert.Equal((ushort)4, MyUInt16Array[0]); + Assert.Equal((uint)5, MyUInt32Array[0]); + Assert.Equal((ulong)6, MyUInt64Array[0]); + Assert.Equal((byte)7, MyByteArray[0]); + Assert.Equal((sbyte)8, MySByteArray[0]); + Assert.Equal('a', MyCharArray[0]); + Assert.Equal("Hello", MyStringArray[0]); + Assert.Equal(3.3m, MyDecimalArray[0]); + Assert.False(MyBooleanFalseArray[0]); + Assert.True(MyBooleanTrueArray[0]); + Assert.Equal(1.1f, MySingleArray[0]); + Assert.Equal(2.2d, MyDoubleArray[0]); + Assert.Equal(new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc), MyDateTimeArray[0]); + Assert.Equal(new DateTimeOffset(2019, 1, 30, 12, 1, 2, new TimeSpan(1, 0, 0)), MyDateTimeOffsetArray[0]); + Assert.Equal(new Guid("1B33498A-7B7D-4DDA-9C13-F6AA4AB449A6"), MyGuidArray[0]); + Assert.Equal(new Uri("https://github.com/dotnet/corefx"), MyUriArray[0]); + Assert.Equal(SampleEnum.Two, MyEnumArray[0]); + + Assert.Equal(10, MyInt16TwoDimensionArray[0][0]); + Assert.Equal(11, MyInt16TwoDimensionArray[0][1]); + Assert.Equal(20, MyInt16TwoDimensionArray[1][0]); + Assert.Equal(21, MyInt16TwoDimensionArray[1][1]); + + Assert.Equal(10, MyInt16TwoDimensionList[0][0]); + Assert.Equal(11, MyInt16TwoDimensionList[0][1]); + Assert.Equal(20, MyInt16TwoDimensionList[1][0]); + Assert.Equal(21, MyInt16TwoDimensionList[1][1]); + + Assert.Equal(11, MyInt16ThreeDimensionArray[0][0][0]); + Assert.Equal(12, MyInt16ThreeDimensionArray[0][0][1]); + Assert.Equal(13, MyInt16ThreeDimensionArray[0][1][0]); + Assert.Equal(14, MyInt16ThreeDimensionArray[0][1][1]); + Assert.Equal(21, MyInt16ThreeDimensionArray[1][0][0]); + Assert.Equal(22, MyInt16ThreeDimensionArray[1][0][1]); + Assert.Equal(23, MyInt16ThreeDimensionArray[1][1][0]); + Assert.Equal(24, MyInt16ThreeDimensionArray[1][1][1]); + + Assert.Equal(11, MyInt16ThreeDimensionList[0][0][0]); + Assert.Equal(12, MyInt16ThreeDimensionList[0][0][1]); + Assert.Equal(13, MyInt16ThreeDimensionList[0][1][0]); + Assert.Equal(14, MyInt16ThreeDimensionList[0][1][1]); + Assert.Equal(21, MyInt16ThreeDimensionList[1][0][0]); + Assert.Equal(22, MyInt16ThreeDimensionList[1][0][1]); + Assert.Equal(23, MyInt16ThreeDimensionList[1][1][0]); + Assert.Equal(24, MyInt16ThreeDimensionList[1][1][1]); + + Assert.Equal("Hello", MyStringList[0]); + + IEnumerator enumerator = MyStringIEnumerable.GetEnumerator(); + enumerator.MoveNext(); + { + // Verifying after deserialization. + if (enumerator.Current is JsonElement currentJsonElement) + { + Assert.Equal("Hello", currentJsonElement.GetString()); + } + // Verifying test data. + else + { + Assert.Equal("Hello", enumerator.Current); + } + } + + { + // Verifying after deserialization. + if (MyStringIList[0] is JsonElement currentJsonElement) + { + Assert.Equal("Hello", currentJsonElement.GetString()); + } + // Verifying test data. + else + { + Assert.Equal("Hello", enumerator.Current); + } + } + + enumerator = MyStringICollection.GetEnumerator(); + enumerator.MoveNext(); + { + // Verifying after deserialization. + if (enumerator.Current is JsonElement currentJsonElement) + { + Assert.Equal("Hello", currentJsonElement.GetString()); + } + // Verifying test data. + else + { + Assert.Equal("Hello", enumerator.Current); + } + } + + Assert.Equal("Hello", MyStringIEnumerableT.First()); + Assert.Equal("Hello", MyStringIListT[0]); + Assert.Equal("Hello", MyStringICollectionT.First()); + Assert.Equal("Hello", MyStringIReadOnlyCollectionT.First()); + Assert.Equal("Hello", MyStringIReadOnlyListT[0]); + Assert.Equal("Hello", MyStringISetT.First()); + + enumerator = MyStringToStringIDict.GetEnumerator(); + enumerator.MoveNext(); + { + // Verifying after deserialization. + if (enumerator.Current is JsonElement currentJsonElement) + { + IEnumerator jsonEnumerator = currentJsonElement.EnumerateObject(); + jsonEnumerator.MoveNext(); + + JsonProperty property = (JsonProperty)jsonEnumerator.Current; + + Assert.Equal("key", property.Name); + Assert.Equal("value", property.Value.GetString()); + } + // Verifying test data. + else + { + DictionaryEntry entry = (DictionaryEntry)enumerator.Current; + Assert.Equal("key", entry.Key); + + if (entry.Value is JsonElement element) + { + Assert.Equal("value", element.GetString()); + } + else + { + Assert.Equal("value", entry.Value); + } + } + } + + Assert.Equal("value", MyStringToStringGenericDict["key"]); + Assert.Equal("value", MyStringToStringGenericIDict["key"]); + Assert.Equal("value", MyStringToStringGenericIReadOnlyDict["key"]); + + Assert.Equal("value", MyStringToStringImmutableDict["key"]); + Assert.Equal("value", MyStringToStringIImmutableDict["key"]); + Assert.Equal("value", MyStringToStringImmutableSortedDict["key"]); + + Assert.Equal("myKey", MyStringToStringKeyValuePair.Key); + Assert.Equal("myValue", MyStringToStringKeyValuePair.Value); + + Assert.Equal(2, MyStringStackT.Count); + Assert.True(MyStringStackT.Contains("Hello")); + Assert.True(MyStringStackT.Contains("World")); + + string[] expectedQueue = { "Hello", "World" }; + int i = 0; + foreach (string item in MyStringQueueT) + { + Assert.Equal(expectedQueue[i], item); + i++; + } + + Assert.Equal("Hello", MyStringHashSetT.First()); + Assert.Equal("Hello", MyStringLinkedListT.First()); + Assert.Equal("Hello", MyStringSortedSetT.First()); + + Assert.Equal("Hello", MyStringIImmutableListT[0]); + Assert.Equal("Hello", MyStringIImmutableStackT.First()); + Assert.Equal("Hello", MyStringIImmutableQueueT.First()); + Assert.Equal("Hello", MyStringIImmutableSetT.First()); + Assert.Equal("Hello", MyStringImmutableHashSetT.First()); + Assert.Equal("Hello", MyStringImmutableListT[0]); + Assert.Equal("Hello", MyStringImmutableStackT.First()); + Assert.Equal("Hello", MyStringImmutablQueueT.First()); + Assert.Equal("Hello", MyStringImmutableSortedSetT.First()); + + Assert.Null(MyListOfNullString[0]); + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests.csproj index 33cb83402e98d9..492366a5da9dba 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests.csproj @@ -84,11 +84,13 @@ + +