<value>Cannot write a comment value which contains the end of comment delimiter.</value>
</data>
<data name="SerializerPropertyNameConflict" xml:space="preserve">
- <value>The property '{0}.{1}' has the same name as a previous property based on naming or casing policies.</value>
+ <value>The JSON property name for '{0}.{1}' collides with another property.</value>
</data>
<data name="SerializerPropertyNameNull" xml:space="preserve">
- <value>The property name for '{0}.{1}' cannot be null as a result of naming policies.</value>
+ <value>The JSON property name for '{0}.{1}' cannot be null.</value>
</data>
</root>
\ No newline at end of file
if (propertyInfo != null)
{
- _propertyRefs.Add(new PropertyRef(GetKey(jsonInfo.CompareName), jsonInfo));
+ _propertyRefs.Add(new PropertyRef(GetKey(jsonInfo.NameUsedToCompare), jsonInfo));
}
else
{
JsonPropertyInfo jsonInfo = (JsonPropertyInfo)Activator.CreateInstance(
propertyInfoClassType,
- BindingFlags.Instance | BindingFlags.NonPublic,
+ BindingFlags.Instance | BindingFlags.Public,
binder: null,
new object[] { parentClassType, declaredPropertyType, runtimePropertyType, propertyInfo, collectionElementType, options },
culture: null);
}
// If the JsonPropertyNameAttribute or naming policy results in collisions, throw an exception.
- if (!propertyNames.Add(jsonPropertyInfo.CompareNameAsString))
+ if (!propertyNames.Add(jsonPropertyInfo.NameUsedToCompareAsString))
{
ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(this, jsonPropertyInfo);
}
{
if (propertyName.Length <= PropertyNameKeyLength ||
// We compare the whole name, although we could skip the first 6 bytes (but it's likely not any faster)
- propertyName.SequenceEqual((ReadOnlySpan<byte>)propertyRef.Info.CompareName))
+ propertyName.SequenceEqual(propertyRef.Info.NameUsedToCompare))
{
info = propertyRef.Info;
return true;
namespace System.Text.Json.Serialization
{
/// <summary>
- /// Determines the naming policy used to convert a JSON name to another format, such as a camel-casing format.
+ /// Determines the naming policy used to convert a string-based name to another format, such as a camel-casing format.
/// </summary>
public abstract class JsonNamingPolicy
{
public static JsonNamingPolicy CamelCase { get; } = new JsonCamelCaseNamePolicy();
/// <summary>
- /// Converts the provided name.
+ /// When overridden in a derived class, converts the specified name according to the policy.
/// </summary>
/// <param name="name">The name to convert.</param>
/// <returns>The converted name.</returns>
private static readonly JsonEnumerableConverter s_jsonArrayConverter = new DefaultArrayConverter();
private static readonly JsonEnumerableConverter s_jsonEnumerableConverter = new DefaultEnumerableConverter();
- internal ClassType ClassType;
+ public ClassType ClassType;
// The name of the property with any casing policy or the name specified from JsonPropertyNameAttribute.
private byte[] _name { get; set; }
- internal ReadOnlySpan<byte> Name => _name;
- internal string NameAsString { get; private set; }
+ public ReadOnlySpan<byte> Name => _name;
+ public string NameAsString { get; private set; }
// Used to support case-insensitive comparison
- private byte[] _compareName { get; set; }
- internal ReadOnlySpan<byte> CompareName => _compareName;
- internal string CompareNameAsString { get; private set; }
+ private byte[] _nameUsedToCompare { get; set; }
+ public ReadOnlySpan<byte> NameUsedToCompare => _nameUsedToCompare;
+ public string NameUsedToCompareAsString { get; private set; }
// The escaped name passed to the writer.
- internal byte[] _escapedName { get; private set; }
- internal ReadOnlySpan<byte> EscapedName => _escapedName;
+ public byte[] _escapedName { get; private set; }
- internal bool HasGetter { get; set; }
- internal bool HasSetter { get; set; }
- internal bool ShouldSerialize { get; private set; }
- internal bool ShouldDeserialize { get; private set; }
+ public bool HasGetter { get; set; }
+ public bool HasSetter { get; set; }
+ public bool ShouldSerialize { get; private set; }
+ public bool ShouldDeserialize { get; private set; }
- internal bool IgnoreNullValues { get; private set; }
+ public bool IgnoreNullValues { get; private set; }
// todo: to minimize hashtable lookups, cache JsonClassInfo:
//public JsonClassInfo ClassInfo;
// Constructor used for internal identifiers
- internal JsonPropertyInfo() { }
+ public JsonPropertyInfo() { }
- internal JsonPropertyInfo(
+ public JsonPropertyInfo(
Type parentClassType,
Type declaredPropertyType,
Type runtimePropertyType,
CanBeNull = IsNullableType || !runtimePropertyType.IsValueType;
}
- internal bool CanBeNull { get; private set; }
- internal JsonClassInfo ElementClassInfo { get; private set; }
- internal JsonEnumerableConverter EnumerableConverter { get; private set; }
+ public bool CanBeNull { get; private set; }
+ public JsonClassInfo ElementClassInfo { get; private set; }
+ public JsonEnumerableConverter EnumerableConverter { get; private set; }
- internal bool IsNullableType { get; private set; }
+ public bool IsNullableType { get; private set; }
- internal PropertyInfo PropertyInfo { get; private set; }
+ public PropertyInfo PropertyInfo { get; private set; }
- internal Type ParentClassType { get; private set; }
+ public Type ParentClassType { get; private set; }
- internal Type DeclaredPropertyType { get; private set; }
+ public Type DeclaredPropertyType { get; private set; }
- internal Type RuntimePropertyType { get; private set; }
+ public Type RuntimePropertyType { get; private set; }
- internal virtual void GetPolicies(JsonSerializerOptions options)
+ public virtual void GetPolicies(JsonSerializerOptions options)
{
DetermineSerializationCapabilities(options);
DeterminePropertyName(options);
if (nameAttribute != null)
{
NameAsString = nameAttribute.Name;
-
- // This is detected and thrown by caller.
+
+ // null is not valid; JsonClassInfo throws an InvalidOperationException after this return.
if (NameAsString == null)
{
return;
{
NameAsString = options.PropertyNamingPolicy.ConvertName(PropertyInfo.Name);
- // This is detected and thrown by caller.
+ // null is not valid; JsonClassInfo throws an InvalidOperationException after this return.
if (NameAsString == null)
{
return;
// Set the compare name.
if (options.PropertyNameCaseInsensitive)
{
- CompareNameAsString = NameAsString.ToUpperInvariant();
- _compareName = Encoding.UTF8.GetBytes(CompareNameAsString);
+ NameUsedToCompareAsString = NameAsString.ToUpperInvariant();
+ _nameUsedToCompare = Encoding.UTF8.GetBytes(NameUsedToCompareAsString);
}
else
{
- CompareNameAsString = NameAsString;
- _compareName = _name;
+ NameUsedToCompareAsString = NameAsString;
+ _nameUsedToCompare = _name;
}
// Cache the escaped name.
+#if true
+ // temporary behavior until the writer can accept escaped string.
+ _escapedName = _name;
+#else
+
int valueIdx = JsonWriterHelper.NeedsEscaping(_name);
if (valueIdx == -1)
{
}
else
{
+ byte[] pooledName = null;
int length = JsonWriterHelper.GetMaxEscapedLength(_name.Length, valueIdx);
- byte[] tempArray = ArrayPool<byte>.Shared.Rent(length);
+ Span<byte> escapedName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc byte[length] :
+ (pooledName = ArrayPool<byte>.Shared.Rent(length));
+
+ JsonWriterHelper.EscapeString(_name, escapedName, 0, out int written);
- JsonWriterHelper.EscapeString(_name, tempArray, valueIdx, out int written);
- _escapedName = new byte[written];
- tempArray.CopyTo(_escapedName, 0);
+ _escapedName = escapedName.Slice(0, written).ToArray();
- // We clear the array because it is "user data" (although a property name).
- new Span<byte>(tempArray, 0, written).Clear();
- ArrayPool<byte>.Shared.Return(tempArray);
+ if (pooledName != null)
+ {
+ // We clear the array because it is "user data" (although a property name).
+ new Span<byte>(pooledName, 0, written).Clear();
+ ArrayPool<byte>.Shared.Return(pooledName);
+ }
}
+#endif
}
}
}
// After the property is added, clear any state not used later.
- internal void ClearUnusedValuesAfterAdd()
+ public void ClearUnusedValuesAfterAdd()
{
NameAsString = null;
- CompareNameAsString = null;
+ NameUsedToCompareAsString = null;
}
// Copy any settings defined at run-time to the new property.
- internal void CopyRuntimeSettingsTo(JsonPropertyInfo other)
+ public void CopyRuntimeSettingsTo(JsonPropertyInfo other)
{
other._name = _name;
- other._compareName = _compareName;
+ other._nameUsedToCompare = _nameUsedToCompare;
other._escapedName = _escapedName;
}
- internal abstract object GetValueAsObject(object obj, JsonSerializerOptions options);
+ public abstract object GetValueAsObject(object obj, JsonSerializerOptions options);
- internal TAttribute GetAttribute<TAttribute>() where TAttribute : Attribute
+ public TAttribute GetAttribute<TAttribute>() where TAttribute : Attribute
{
return (TAttribute)PropertyInfo?.GetCustomAttribute(typeof(TAttribute), inherit: false);
}
- internal abstract void ApplyNullValue(JsonSerializerOptions options, ref ReadStack state);
-
- internal abstract IList CreateConverterList();
+ public abstract void ApplyNullValue(JsonSerializerOptions options, ref ReadStack state);
+
+ public abstract IList CreateConverterList();
- internal abstract Type GetConcreteType(Type interfaceType);
+ public abstract Type GetConcreteType(Type interfaceType);
- internal abstract void Read(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader);
- internal abstract void ReadEnumerable(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader);
- internal abstract void SetValueAsObject(object obj, object value, JsonSerializerOptions options);
+ public abstract void Read(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader);
+ public abstract void ReadEnumerable(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader);
+ public abstract void SetValueAsObject(object obj, object value, JsonSerializerOptions options);
- internal abstract void Write(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer);
+ public abstract void Write(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer);
- internal abstract void WriteDictionary(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer);
- internal abstract void WriteEnumerable(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer);
+ public abstract void WriteDictionary(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer);
+ public abstract void WriteEnumerable(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer);
}
}
/// </summary>
internal abstract class JsonPropertyInfoCommon<TClass, TDeclaredProperty, TRuntimeProperty> : JsonPropertyInfo
{
- internal bool _isPropertyPolicy;
- internal Func<TClass, TDeclaredProperty> Get { get; private set; }
- internal Action<TClass, TDeclaredProperty> Set { get; private set; }
+ public bool _isPropertyPolicy;
+ public Func<TClass, TDeclaredProperty> Get { get; private set; }
+ public Action<TClass, TDeclaredProperty> Set { get; private set; }
public JsonValueConverter<TRuntimeProperty> ValueConverter { get; internal set; }
// Constructor used for internal identifiers
- internal JsonPropertyInfoCommon() { }
+ public JsonPropertyInfoCommon() { }
- internal JsonPropertyInfoCommon(
+ public JsonPropertyInfoCommon(
Type parentClassType,
Type declaredPropertyType,
Type runtimePropertyType,
GetPolicies(options);
}
- internal override void GetPolicies(JsonSerializerOptions options)
+ public override void GetPolicies(JsonSerializerOptions options)
{
ValueConverter = DefaultConverters<TRuntimeProperty>.s_converter;
base.GetPolicies(options);
}
- internal override object GetValueAsObject(object obj, JsonSerializerOptions options)
+ public override object GetValueAsObject(object obj, JsonSerializerOptions options)
{
if (_isPropertyPolicy)
{
return Get((TClass)obj);
}
- internal override void SetValueAsObject(object obj, object value, JsonSerializerOptions options)
+ public override void SetValueAsObject(object obj, object value, JsonSerializerOptions options)
{
Debug.Assert(Set != null);
TDeclaredProperty typedValue = (TDeclaredProperty)value;
}
}
- internal override IList CreateConverterList()
+ public override IList CreateConverterList()
{
return new List<TDeclaredProperty>();
}
// Map interfaces to a well-known implementation.
- internal override Type GetConcreteType(Type interfaceType)
+ public override Type GetConcreteType(Type interfaceType)
{
if (interfaceType.IsAssignableFrom(typeof(IDictionary<string, TRuntimeProperty>)) ||
interfaceType.IsAssignableFrom(typeof(IReadOnlyDictionary<string, TRuntimeProperty>)))
where TRuntimeProperty : TDeclaredProperty
{
// Constructor used for internal identifiers
- internal JsonPropertyInfoNotNullable() { }
+ public JsonPropertyInfoNotNullable() { }
- internal JsonPropertyInfoNotNullable(
+ public JsonPropertyInfoNotNullable(
Type parentClassType,
Type declaredPropertyType,
Type runtimePropertyType,
{
}
- internal override void Read(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader)
+ public override void Read(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader)
{
if (ElementClassInfo != null)
{
}
// If this method is changed, also change JsonPropertyInfoNullable.ReadEnumerable and JsonSerializer.ApplyObjectToEnumerable
- internal override void ReadEnumerable(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader)
+ public override void ReadEnumerable(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader)
{
if (ValueConverter == null || !ValueConverter.TryRead(RuntimePropertyType, ref reader, out TRuntimeProperty value))
{
JsonSerializer.ApplyValueToEnumerable(ref value, options, ref state.Current);
}
- internal override void ApplyNullValue(JsonSerializerOptions options, ref ReadStack state)
+ public override void ApplyNullValue(JsonSerializerOptions options, ref ReadStack state)
{
Debug.Assert(state.Current.JsonPropertyInfo != null);
state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, null, options);
}
// todo: have the caller check if current.Enumerator != null and call WriteEnumerable of the underlying property directly to avoid an extra virtual call.
- internal override void Write(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
+ public override void Write(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
{
if (current.Enumerator != null)
{
}
}
- internal override void WriteDictionary(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
+ public override void WriteDictionary(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
{
JsonSerializer.WriteDictionary(ValueConverter, options, ref current, writer);
}
- internal override void WriteEnumerable(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
+
+ public override void WriteEnumerable(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
{
if (ValueConverter != null)
{
// should this be cached somewhere else so that it's not populated per TClass as well as TProperty?
private static readonly Type s_underlyingType = typeof(TProperty);
- internal JsonPropertyInfoNullable(
+ public JsonPropertyInfoNullable(
Type parentClassType,
Type declaredPropertyType,
Type runtimePropertyType,
{
}
- internal override void Read(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader)
+ public override void Read(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader)
{
if (ElementClassInfo != null)
{
}
}
- internal override void ReadEnumerable(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader)
+ public override void ReadEnumerable(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader)
{
if (ValueConverter == null || !ValueConverter.TryRead(typeof(TProperty), ref reader, out TProperty value))
{
JsonSerializer.ApplyValueToEnumerable(ref nullableValue, options, ref state.Current);
}
- internal override void ApplyNullValue(JsonSerializerOptions options, ref ReadStack state)
+ public override void ApplyNullValue(JsonSerializerOptions options, ref ReadStack state)
{
TProperty? nullableValue = null;
JsonSerializer.ApplyValueToEnumerable(ref nullableValue, options, ref state.Current);
}
// todo: have the caller check if current.Enumerator != null and call WriteEnumerable of the underlying property directly to avoid an extra virtual call.
- internal override void Write(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
+ public override void Write(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
{
if (current.Enumerator != null)
{
}
}
- internal override void WriteDictionary(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
+ public override void WriteDictionary(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
{
JsonSerializer.WriteDictionary(ValueConverter, options, ref current, writer);
}
- internal override void WriteEnumerable(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
+ public override void WriteEnumerable(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
{
if (ValueConverter != null)
{
Debug.Assert(state.Current.ReturnValue != default);
Debug.Assert(state.Current.JsonClassInfo != default);
- ReadOnlySpan<byte> propertyName = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
-
if (state.Current.IsDictionary())
{
string keyName = reader.GetString();
}
else
{
+ ReadOnlySpan<byte> propertyName = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
+ if (reader._stringHasEscaping)
+ {
+ int idx = propertyName.IndexOf(JsonConstants.BackSlash);
+ Debug.Assert(idx != -1);
+ propertyName = GetUnescapedString(propertyName, idx);
+ }
+
state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(options, propertyName, ref state.Current);
if (state.Current.JsonPropertyInfo == null)
{
return;
}
+
+ private static ReadOnlySpan<byte> GetUnescapedString(ReadOnlySpan<byte> utf8Source, int idx)
+ {
+ // The escaped name is always longer than the unescaped, so it is safe to use escaped name for the buffer length.
+ int length = utf8Source.Length;
+ byte[] pooledName = null;
+
+ Span<byte> unescapedName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc byte[length] :
+ (pooledName = ArrayPool<byte>.Shared.Rent(length));
+
+ JsonReaderHelper.Unescape(utf8Source, unescapedName, idx, out int written);
+ ReadOnlySpan<byte> propertyName = unescapedName.Slice(0, written).ToArray();
+
+ if (pooledName != null)
+ {
+ // We clear the array because it is "user data" (although a property name).
+ new Span<byte>(pooledName, 0, written).Clear();
+ ArrayPool<byte>.Shared.Return(pooledName);
+ }
+
+ return propertyName;
+ }
}
}
}
else
{
+#if true
+ // temporary behavior until the writer can accept escaped string.
+ byte[] utf8Key = Encoding.UTF8.GetBytes(key);
+ converter.Write(utf8Key, value, writer);
+#else
byte[] pooledKey = null;
byte[] utf8Key = Encoding.UTF8.GetBytes(key);
int length = JsonWriterHelper.GetMaxEscapedLength(utf8Key.Length, 0);
if (pooledKey != null)
{
+ // We clear the array because it is "user data" (although a property name).
+ new Span<byte>(pooledKey, 0, written).Clear();
ArrayPool<byte>.Shared.Return(pooledKey);
}
+#endif
}
}
}
// todo: this should throw a JsonReaderException
Assert.Throws<ArgumentException>(() => JsonSerializer.Parse<Dictionary<string, string>>(@"{""Hello"":""World"", ""Hello"":""World""}"));
}
+
+ [Fact]
+ public static void UnicodePropertyNames()
+ {
+ {
+ Dictionary<string, int> obj = JsonSerializer.Parse<Dictionary<string, int>>(@"{""Aѧ"":1}");
+ Assert.Equal(1, obj["Aѧ"]);
+
+ // Verify the name is escaped after serialize.
+ string json = JsonSerializer.ToString(obj);
+ Assert.Equal(@"{""A\u0467"":1}", json);
+ }
+
+ {
+ // We want to go over StackallocThreshold=256 to force a pooled allocation, so this property is 200 chars and 400 bytes.
+ const int charsInProperty = 200;
+
+ string longPropertyName = new string('ѧ', charsInProperty);
+
+ Dictionary<string, int> obj = JsonSerializer.Parse<Dictionary<string, int>>($"{{\"{longPropertyName}\":1}}");
+ Assert.Equal(1, obj[longPropertyName]);
+
+ // Verify the name is escaped after serialize.
+ string json = JsonSerializer.ToString(obj);
+
+ // Duplicate the unicode character 'charsInProperty' times.
+ string longPropertyNameEscaped = new StringBuilder().Insert(0, @"\u0467", charsInProperty).ToString();
+
+ string expectedJson = $"{{\"{longPropertyNameEscaped}\":1}}";
+ Assert.Equal(expectedJson, json);
+
+ // Verify the name is unescaped after deserialize.
+ obj = JsonSerializer.Parse<Dictionary<string, int>>(json);
+ Assert.Equal(1, obj[longPropertyName]);
+ }
+ }
}
}
// 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.Generic;
+using System.Text.Json.Tests;
using Xunit;
namespace System.Text.Json.Serialization.Tests
Assert.Equal(1, obj.MyInt1);
}
}
+
+ [Fact]
+ public static void UnicodePropertyNames()
+ {
+ {
+ ClassWithUnicodeProperty obj = JsonSerializer.Parse<ClassWithUnicodeProperty>(@"{""Aѧ"":1}");
+ Assert.Equal(1, obj.Aѧ);
+
+ // Verify the name is escaped after serialize.
+ string json = JsonSerializer.ToString(obj);
+ Assert.Contains(@"""A\u0467"":1", json);
+
+ // Verify the name is unescaped after deserialize.
+ obj = JsonSerializer.Parse<ClassWithUnicodeProperty>(json);
+ Assert.Equal(1, obj.Aѧ);
+ }
+
+ {
+ // We want to go over StackallocThreshold=256 to force a pooled allocation, so this property is 400 chars and 401 bytes.
+ ClassWithUnicodeProperty obj = JsonSerializer.Parse<ClassWithUnicodeProperty>(@"{""Aѧ34567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"":1}");
+ Assert.Equal(1, obj.Aѧ34567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890);
+
+ // Verify the name is escaped after serialize.
+ string json = JsonSerializer.ToString(obj);
+ Assert.Contains(@"""A\u046734567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"":1", json);
+
+ // Verify the name is unescaped after deserialize.
+ obj = JsonSerializer.Parse<ClassWithUnicodeProperty>(json);
+ Assert.Equal(1, obj.Aѧ34567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890);
+ }
+ }
}
public class OverridePropertyNameDesignTime_TestClass
Assert.Equal(98052, mainSite.zip);
}
}
+
+ public class ClassWithUnicodeProperty
+ {
+ public int Aѧ { get; set; }
+
+ // A 400 character property name with a unicode character making it 401 bytes.
+ public int Aѧ34567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 { get; set; }
+ }
}