使用 .NET 4.8 Framework 和 System.Text.Json v.9.0,我尝试使用自定义转换器在序列化时添加注释(如本文中所示)
如何向 Json.NET 输出添加注释?
public class JsonCommentConverter : JsonConverter
{
private readonly string _comment;
public JsonCommentConverter(string comment)
{
_comment = comment;
}
...
}
public class Person
{
[JsonConverter(typeof(JsonCommentConverter), "Name of the person")]
public string Name { get; set; }
[JsonConverter(typeof(JsonCommentConverter), "Age of the person")]
public int Age { get; set; }
}
不幸的是,这是一篇旧文章,JsonConverter 不允许传递 2 个参数。
我知道 JSON 标准不允许注释,但是有没有办法在序列化点添加注释?
基本上,这就是我想要的
{
"Name": "Jack"/*Name of the person*/,
"Age": 22/*Age of the person*/
}
谢谢你,
JsonConverterAttribute
确实不支持传递构造函数参数,但它不是密封的,因此您可以创建自己的属性,该属性派生自它并使用所需的注释作为参数。然后覆盖 JsonConverterAttribute.CreateConverter(Type)
以返回您的 JsonCommentConverter
,并将注释传递到其构造函数中。
为此,首先创建以下自定义属性:
public sealed class JsonCommentAttribute : JsonConverterAttribute
{
readonly string Comment;
public JsonCommentAttribute(string Comment) { this.Comment = Comment; }
public override JsonConverter CreateConverter (Type typeToConvert) { return new JsonCommentConverter(Comment); }
}
接下来创建
JsonCommentConverter
如下:
public class JsonCommentConverter<TBase> : DefaultConverterFactory<TBase>
{
readonly string CommentWithDelimiters;
public JsonCommentConverter(string comment)
{
this.CommentWithDelimiters = " /*" + comment + "*/";
}
protected override void Write<T>(Utf8JsonWriter writer, T value, JsonSerializerOptions modifiedOptions)
{
// TODO: in .NET 9 investigate https://learn.microsoft.com/en-us/dotnet/api/microsoft.toolkit.highperformance.buffers.arraypoolbufferwriter-1 to avoid string allocations.
var json = JsonSerializer.Serialize(value, modifiedOptions);
writer.WriteRawValue(json + CommentWithDelimiters, skipInputValidation : true);
}
protected override JsonSerializerOptions ModifyOptions(JsonSerializerOptions options)
{
var modifiedOptions = base.ModifyOptions(options);
modifiedOptions.WriteIndented = false;
return modifiedOptions;
}
}
public abstract class DefaultConverterFactory<TBase> : JsonConverterFactory
{
// Adapted from this answer https://stackoverflow.com/a/78512783/3744182
// To https://stackoverflow.com/questions/78507408/in-system-text-json-is-it-possible-to-minify-only-array-items
class DefaultConverter<TConcrete> : JsonConverter<TConcrete> where TConcrete : TBase
{
readonly JsonSerializerOptions modifiedOptions;
readonly DefaultConverterFactory<TBase> factory;
public DefaultConverter(JsonSerializerOptions modifiedOptions, DefaultConverterFactory<TBase> factory) { this.modifiedOptions = modifiedOptions; this.factory = factory; }
public override void Write(Utf8JsonWriter writer, TConcrete value, JsonSerializerOptions options) { factory.Write(writer, value, modifiedOptions); }
// In .NET 9 use
// return public override TConcrete? Read
public override TConcrete Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return factory.Read<TConcrete>(ref reader, typeToConvert, modifiedOptions); }
}
protected virtual JsonSerializerOptions ModifyOptions(JsonSerializerOptions options) { return options.CopyAndRemoveConverter(this.GetType()); }
// In .NET 9 use
// return public override T? Read(
protected virtual TConcrete Read<TConcrete>(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions) where TConcrete : TBase
{
// In .NET 9 use
// return (T?)
return (TConcrete)JsonSerializer.Deserialize(ref reader, typeToConvert, modifiedOptions);
}
protected virtual void Write<TConcrete>(Utf8JsonWriter writer, TConcrete value, JsonSerializerOptions modifiedOptions) where TConcrete : TBase
{
JsonSerializer.Serialize(writer, value, modifiedOptions);
}
public sealed override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
var modifiedOptions = ModifyOptions(options);
if (typeToConvert == typeof(TBase))
return new DefaultConverter<TBase>(modifiedOptions, this);
else
// In .NET 9 apply ! to the end of this line to suppress a nullable warning
return (JsonConverter)Activator.CreateInstance(typeof(DefaultConverter<>).MakeGenericType(typeof(TBase), typeToConvert), new object [] {modifiedOptions, this });
}
public override bool CanConvert(Type typeToConvert) { return typeof(TBase).IsAssignableFrom(typeToConvert); }
}
public static partial class JsonExtensions
{
public static JsonSerializerOptions CopyAndRemoveConverter(this JsonSerializerOptions options, Type converterType)
{
var copy = new JsonSerializerOptions(options);
for (var i = copy.Converters.Count - 1; i >= 0; i--)
if (copy.Converters[i].GetType() == converterType)
copy.Converters.RemoveAt(i);
return copy;
}
}
最后修改
Person
如下:
public class Person
{
[JsonComment("Name of the person")]
public string Name { get; set; }
[JsonComment("Age of the person")]
public int Age { get; set; }
}
并使用以下
JsonSerializerOptions
对其进行序列化:
var person = new Person { Name = "Jack", Age = 22 };
var options = new JsonSerializerOptions
{
ReadCommentHandling = JsonCommentHandling.Skip,
// Add other options as required, e.g.
WriteIndented = true,
};
var json = JsonSerializer.Serialize(person, options);
var person2 = JsonSerializer.Deserialize<Person>(json, options);
您的 JSON 将根据需要:
{
"Name": "Jack" /*Name of the person*/,
"Age": 22 /*Age of the person*/
}
备注:
严格来说,JSON标准中不包含注释,System.Text.Json默认不允许注释。 不过,您可以通过设置
JsonCommentHandling
来启用支持,如如何使用 System.Text.Json 解析带有注释的 JSON?.
我在 .NET 9 中测试了我的解决方案,但您使用的是 .NET 4.8 Framework 和 System.Text.Json v.9.0。 这是一个不寻常的组合,Microsoft 似乎没有很好地支持它,因此您可能会遇到问题。 由于
Utf8JsonReader
是在 C# 7.2中首次引入的
ref struct
,因此在构建时必须确保使用此语言版本或更高版本。 要了解如何操作,请查看 。NET Framework 4.8 似乎使用早于 7 的 C# 版本。
如果您确实遇到问题,例如spans 或 ref structs,我建议坚持使用 .NET Framework 4.x 中的 Newtonsoft。 正如您所发现的,Newtonsoft 确实支持通过转换器向属性添加注释,请参阅如何向 Json.NET 输出添加注释?了解详细信息。
在编写上面的代码时,我尝试不使用 7.3 以上的任何 C# 语言功能,但我没有可用于测试 .NET 4.8 Framework 和 System.Text.Json v.9.0 组合的工具。
由于您的目标是 .NET Framework,所以我删除了所有可为 null 的注释,但在 .NET 9 中,人们希望应用它们。
Utf8JsonWriter.WriteCommentValue()
,如下所示:
JsonSerializer.Serialize(writer, value, options);
writer.WriteCommentValue(Comment);
导致评论被放置在下一行并缩进:
{
"Name": "Jack"
/*Name of the person*/,
"Age": 22
/*Age of the person*/
}
由于您的问题显示评论出现在同一行,所以我必须调整答案 In System.Text.Json is it possible to minify only array items? 暂时禁用缩进。
虽然这对您来说并不重要,但在
源生成模式中不支持从
JsonPropertyAttribute
派生。 使用源生成时需要不同的实现。
演示 .NET 9 在这里摆弄:https://dotnetfiddle.net/Y2p2I5