Skip to content

Commit

Permalink
Add property ordering to source gen (#55662)
Browse files Browse the repository at this point in the history
  • Loading branch information
steveharter committed Jul 14, 2021
1 parent 3ec6907 commit b412aac
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 3 deletions.
19 changes: 19 additions & 0 deletions src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ private sealed class Parser

private const string JsonPropertyNameAttributeFullName = "System.Text.Json.Serialization.JsonPropertyNameAttribute";

private const string JsonPropertyOrderAttributeFullName = "System.Text.Json.Serialization.JsonPropertyOrderAttribute";

private readonly GeneratorExecutionContext _executionContext;

private readonly MetadataLoadContextInternal _metadataLoadContext;
Expand Down Expand Up @@ -606,6 +608,8 @@ private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type, JsonSourceGener
BindingFlags.NonPublic |
BindingFlags.DeclaredOnly;

bool propertyOrderSpecified = false;

for (Type? currentType = type; currentType != null; currentType = currentType.BaseType)
{
foreach (PropertyInfo propertyInfo in currentType.GetProperties(bindingFlags))
Expand All @@ -620,6 +624,7 @@ private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type, JsonSourceGener

PropertyGenerationSpec spec = GetPropertyGenerationSpec(propertyInfo, isVirtual, generationMode);
CacheMember(spec, ref propGenSpecList, ref ignoredMembers);
propertyOrderSpecified |= spec.Order != 0;
}

foreach (FieldInfo fieldInfo in currentType.GetFields(bindingFlags))
Expand All @@ -631,8 +636,14 @@ private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type, JsonSourceGener

PropertyGenerationSpec spec = GetPropertyGenerationSpec(fieldInfo, isVirtual: false, generationMode);
CacheMember(spec, ref propGenSpecList, ref ignoredMembers);
propertyOrderSpecified |= spec.Order != 0;
}
}

if (propertyOrderSpecified)
{
propGenSpecList.Sort((p1, p2) => p1.Order.CompareTo(p2.Order));
}
}

typeMetadata.Initialize(
Expand Down Expand Up @@ -697,6 +708,7 @@ private PropertyGenerationSpec GetPropertyGenerationSpec(MemberInfo memberInfo,

bool foundDesignTimeCustomConverter = false;
string? converterInstantiationLogic = null;
int order = 0;

foreach (CustomAttributeData attributeData in attributeDataList)
{
Expand Down Expand Up @@ -745,6 +757,12 @@ private PropertyGenerationSpec GetPropertyGenerationSpec(MemberInfo memberInfo,
// Null check here is done at runtime within JsonSerializer.
}
break;
case JsonPropertyOrderAttributeFullName:
{
IList<CustomAttributeTypedArgument> ctorArgs = attributeData.ConstructorArguments;
order = (int)ctorArgs[0].Value;
}
break;
default:
break;
}
Expand Down Expand Up @@ -839,6 +857,7 @@ private PropertyGenerationSpec GetPropertyGenerationSpec(MemberInfo memberInfo,
SetterIsVirtual = setterIsVirtual,
DefaultIgnoreCondition = ignoreCondition,
NumberHandling = numberHandling,
Order = order,
HasJsonInclude = hasJsonInclude,
TypeGenerationSpec = GetOrAddTypeGenerationSpec(memberCLRType, generationMode),
DeclaringTypeRef = $"global::{memberInfo.DeclaringType.GetUniqueCompilableTypeName()}",
Expand Down
5 changes: 5 additions & 0 deletions src/libraries/System.Text.Json/gen/PropertyGenerationSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ internal sealed class PropertyGenerationSpec
/// </summary>
public JsonNumberHandling? NumberHandling { get; init; }

/// <summary>
/// The serialization order of the property.
/// </summary>
public int Order { get; init; }

/// <summary>
/// Whether the property has the JsonIncludeAttribute. If so, non-public accessors can be used for (de)serialziation.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public interface ITestContext
public JsonTypeInfo<MyType> MyType { get; }
public JsonTypeInfo<MyType2> MyType2 { get; }
public JsonTypeInfo<MyTypeWithCallbacks> MyTypeWithCallbacks { get; }
public JsonTypeInfo<MyTypeWithPropertyOrdering> MyTypeWithPropertyOrdering { get; }
public JsonTypeInfo<MyIntermediateType> MyIntermediateType { get; }
public JsonTypeInfo<HighLowTempsImmutable> HighLowTempsImmutable { get; }
public JsonTypeInfo<RealWorldContextTests.MyNestedClass> MyNestedClass { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace System.Text.Json.SourceGeneration.Tests
[JsonSerializable(typeof(MyType))]
[JsonSerializable(typeof(MyType2))]
[JsonSerializable(typeof(MyTypeWithCallbacks))]
[JsonSerializable(typeof(MyTypeWithPropertyOrdering))]
[JsonSerializable(typeof(MyIntermediateType))]
[JsonSerializable(typeof(HighLowTempsImmutable))]
[JsonSerializable(typeof(RealWorldContextTests.MyNestedClass))]
Expand Down Expand Up @@ -47,6 +48,7 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.NotNull(MetadataAndSerializationContext.Default.MyType.Serialize);
Assert.NotNull(MetadataAndSerializationContext.Default.MyType2.Serialize);
Assert.NotNull(MetadataAndSerializationContext.Default.MyTypeWithCallbacks.Serialize);
Assert.NotNull(MetadataAndSerializationContext.Default.MyTypeWithPropertyOrdering.Serialize);
Assert.NotNull(MetadataAndSerializationContext.Default.MyIntermediateType.Serialize);
Assert.NotNull(MetadataAndSerializationContext.Default.HighLowTempsImmutable.Serialize);
Assert.NotNull(MetadataAndSerializationContext.Default.MyNestedClass.Serialize);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace System.Text.Json.SourceGeneration.Tests
[JsonSerializable(typeof(MyType), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(MyType2), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(MyTypeWithCallbacks), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(MyTypeWithPropertyOrdering), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(HighLowTempsImmutable), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(RealWorldContextTests.MyNestedClass), GenerationMode = JsonSourceGenerationMode.Metadata)]
Expand Down Expand Up @@ -67,6 +68,7 @@ public override void EnsureFastPathGeneratedAsExpected()
[JsonSerializable(typeof(MyType))]
[JsonSerializable(typeof(MyType2))]
[JsonSerializable(typeof(MyTypeWithCallbacks))]
[JsonSerializable(typeof(MyTypeWithPropertyOrdering))]
[JsonSerializable(typeof(MyIntermediateType))]
[JsonSerializable(typeof(HighLowTempsImmutable))]
[JsonSerializable(typeof(RealWorldContextTests.MyNestedClass))]
Expand Down Expand Up @@ -96,6 +98,7 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.Null(MetadataContext.Default.MyType.Serialize);
Assert.Null(MetadataContext.Default.MyType2.Serialize);
Assert.Null(MetadataContext.Default.MyTypeWithCallbacks.Serialize);
Assert.Null(MetadataContext.Default.MyTypeWithPropertyOrdering.Serialize);
Assert.Null(MetadataContext.Default.MyIntermediateType.Serialize);
Assert.Null(MetadataContext.Default.HighLowTempsImmutable.Serialize);
Assert.Null(MetadataContext.Default.MyNestedClass.Serialize);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace System.Text.Json.SourceGeneration.Tests
[JsonSerializable(typeof(MyType), GenerationMode = JsonSourceGenerationMode.Default)]
[JsonSerializable(typeof(MyType2), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(MyTypeWithCallbacks), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(MyTypeWithPropertyOrdering), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(HighLowTempsImmutable), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(RealWorldContextTests.MyNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)]
Expand Down Expand Up @@ -45,6 +46,7 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.NotNull(MixedModeContext.Default.MyType.Serialize);
Assert.NotNull(MixedModeContext.Default.MyType2.Serialize);
Assert.NotNull(MixedModeContext.Default.MyTypeWithCallbacks.Serialize);
Assert.NotNull(MixedModeContext.Default.MyTypeWithPropertyOrdering.Serialize);
Assert.NotNull(MixedModeContext.Default.MyIntermediateType.Serialize);
Assert.Null(MixedModeContext.Default.HighLowTempsImmutable.Serialize);
Assert.NotNull(MixedModeContext.Default.MyNestedClass.Serialize);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -561,5 +561,13 @@ protected static void AssertFastPathLogicCorrect<T>(string expectedJson, T value

JsonTestHelper.AssertJsonEqual(expectedJson, Encoding.UTF8.GetString(ms.ToArray()));
}

[Fact]
public void PropertyOrdering()
{
MyTypeWithPropertyOrdering obj = new();
string json = JsonSerializer.Serialize(obj, DefaultContext.MyTypeWithPropertyOrdering);
Assert.Equal("{\"C\":0,\"B\":0,\"A\":0}", json);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace System.Text.Json.SourceGeneration.Tests
[JsonSerializable(typeof(MyType))]
[JsonSerializable(typeof(MyType2))]
[JsonSerializable(typeof(MyTypeWithCallbacks))]
[JsonSerializable(typeof(MyTypeWithPropertyOrdering))]
[JsonSerializable(typeof(MyIntermediateType))]
[JsonSerializable(typeof(HighLowTempsImmutable))]
[JsonSerializable(typeof(RealWorldContextTests.MyNestedClass))]
Expand All @@ -40,6 +41,7 @@ internal partial class SerializationContext : JsonSerializerContext, ITestContex
[JsonSerializable(typeof(MyType), GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(MyType2), GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(MyTypeWithCallbacks), GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(MyTypeWithPropertyOrdering), GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(HighLowTempsImmutable), GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(RealWorldContextTests.MyNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)]
Expand All @@ -63,6 +65,7 @@ internal partial class SerializationWithPerTypeAttributeContext : JsonSerializer
[JsonSerializable(typeof(MyType), GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(MyType2), GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(MyTypeWithCallbacks), GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(MyTypeWithPropertyOrdering), GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(HighLowTempsImmutable), GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(RealWorldContextTests.MyNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)]
Expand Down Expand Up @@ -97,6 +100,7 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.NotNull(SerializationContext.Default.MyType.Serialize);
Assert.NotNull(SerializationContext.Default.MyType2.Serialize);
Assert.NotNull(SerializationContext.Default.MyTypeWithCallbacks.Serialize);
Assert.NotNull(SerializationContext.Default.MyTypeWithPropertyOrdering.Serialize);
Assert.NotNull(SerializationContext.Default.MyIntermediateType.Serialize);
Assert.NotNull(SerializationContext.Default.HighLowTempsImmutable.Serialize);
Assert.NotNull(SerializationContext.Default.MyNestedClass.Serialize);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ public class MyTypeWithCallbacks : IJsonOnSerializing, IJsonOnSerialized
void IJsonOnSerialized.OnSerialized() => MyProperty = "After";
}

public class MyTypeWithPropertyOrdering
{
public int B { get; set; }

[JsonPropertyOrder(1)]
public int A { get; set; }

[JsonPropertyOrder(-1)]
[JsonInclude]
public int C = 0;
}

public class JsonMessage
{
public string Message { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ private class MyPoco_BeforeAndAfter
public int A { get; set; }

[JsonPropertyOrder(-1)]
public int C { get; set; }
[JsonInclude]
public int C = 0;
}

[Fact]
Expand All @@ -28,7 +29,8 @@ public static void BeforeAndAfterDefaultOrder()
private class MyPoco_After
{
[JsonPropertyOrder(2)]
public int C { get; set; }
[JsonInclude]
public int C = 0;

public int B { get; set; }
public int D { get; set; }
Expand All @@ -48,7 +50,8 @@ public static void AfterDefaultOrder()
private class MyPoco_Before
{
[JsonPropertyOrder(-1)]
public int C { get; set; }
[JsonInclude]
public int C = 0;

public int B { get; set; }
public int D { get; set; }
Expand Down

0 comments on commit b412aac

Please sign in to comment.