I'm currently using the following code to set up a System.Text.Json JsonSerializerOptions object that alphabetizes properties upon serialization:
public static readonly JsonSerializerOptions serializerOptions = new()
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers =
{
AlphabetizeProperties,
}
}
}
private static void AlphabetizeProperties(JsonTypeInfo info)
{
if (info.Kind != JsonTypeInfoKind.Object) return;
List<JsonPropertyInfo> sortedProperties = [];
sortedProperties.AddRange(info.Properties);
sortedProperties.Sort((p1, p2) => string.CompareOrdinal(p1.Name, p2.Name));
info.Properties.Clear();
for (int i = 0; i < sortedProperties.Count; i++)
{
sortedProperties[i].Order = i;
info.Properties.Add(sortedProperties[i]);
}
}
I'm in the process of trying to convert our code to use System.Text.Json's source generation feature so that we can turn on trimming and eventually move to NativeAOT. However, I cannot find any way to insert modifiers into the JsonSerializerContext.
Things I've tried:
- The
JsonSourceGenerationOptionsattribute has no way to insert modifiers. - I tried using
WithAddedModifieron myJsonSerializerContext. I can, in a static constructor, initialize theJsonSerializerOptions, manually construct a new instance of myJsonSerializerContext, and overwrite theDefaultproperty. This is similar to the approach used here, for a different purpose. However, the dependency is circular; I need theJsonSerializerOptionsto initialize theJsonSerializerContextso I can callWithAddedModifierson it, and I need to pass theJsonSerializerContexttoJsonSerializerOptions.TypeInfoResolverto initialize it. - I tried obtaining the
JsonTypeInfoinstance viaJsonSerializerContext.Default.MyObjectand callingAlphabetizePropertiesdirectly on it. This throws anInvalidOperationExceptionwith the following message:This JsonTypeInfo instance is marked read-only or has already been used in serialization or deserialization.
At this point, it seems my only option is to copy the source generator wholesale and re-order the properties within my new generator. Short of taking this drastic step, is there any way to achieve what I'm looking for?