使用 System.Text.Json 有条件地将对象序列化为单个字符串
use System.Text.Json to serialize an Object as a single String conditionally
我正在使用 C# 实现 ActivityPub 实现,有时 link 是“字符串”,例如 url link,有时 link 是具有 Link 子类型的对象。 (Link:实体)
我想知道如果一组特定条件为真,是否有可能使用 System.Text.Json 将 Link 对象序列化为字符串(只需将一个字符串写入作者) , 如果条件不成立,则将整个默认对象写入 writer。
我已尝试遵循此解决方案:,它仍然适用于代码 fiddle,但不适用于我的实现,我不太清楚为什么。
有谁知道我该如何调试它,或者有更好的方法来确保 Link : Entity
对象有时可以序列化为字符串?
我得到以下错误:
(在这种情况下,我什至尝试将获取的默认 ctor 添加到 modifiedOptions)
大意,它说 Link class 没有数据映射。
我还尝试将 JsonSerializeable 属性直接添加到 Link class.
错误:
Metadata for type 'ActivityPub.Types.Link' was not provided to the serializer. The serializer method used does not support reflection-based creation of serialization-related type metadata. If using source generation, ensure that all root types passed to the serializer have been indicated with 'JsonSerializableAttribute', along with any types that might be serialized polymorphically.
我的基础代码库:https://github.com/Meep-Tech/ActivityHub.Net/tree/collapse_links_during_serialization
测试代码:
static void Main(string[] args) {
Settings.DefaultContext = new Link("ActivityPub.Net.Testing");
var testObject = new Object {
Type = "Test",
At = new Link("/terry") {
Rels = new string[] {
"test",
"test2"
}
},
Attribution = "/meep",
Audience = new Link("/all") {
Rel = "test"
}
};
string json = testObject
.Serialize();
System.IO.File.WriteAllLines(
"test.json",
new[] { json }
);
Object @object = json.DeSerializeEntity<Object>();
System.IO.File.WriteAllLines(
"test1.json",
new[] { @object.ToString() }
);
}
在我的 original version of DefaultConverterFactory<T>
, I cached the default converter because, in its documentation How to write custom converters for JSON serialization (marshalling) in .NET 中,Microsoft 建议在序列化复杂对象时出于性能原因缓存任何所需的转换器:
public DictionaryEnumConverterInner(JsonSerializerOptions options)
{
// For performance, use the existing converter if available.
_valueConverter = (JsonConverter<TValue>)options
.GetConverter(typeof(TValue));
// Cache the key and value types.
_keyType = typeof(TKey);
_valueType = typeof(TValue);
}
但是,由于以下几个原因,这已被证明是有问题的:
当使用 声明的 类型 object
序列化多态值时,GetConverter()
返回一个非功能转换器。
序列化数值时,返回的转换器忽略NumberHandling
设置。
现在看来您可能遇到了第三个问题:使用compile-time serializer source generation时,返回的转换器可能无法工作。
有足够的问题可以忽略 Microsoft 的建议。只需DefaultConverter<T>
如下:
public abstract class DefaultConverterFactory<T> : JsonConverterFactory
{
class DefaultConverter : JsonConverter<T>
{
readonly JsonSerializerOptions modifiedOptions;
readonly DefaultConverterFactory<T> factory;
public DefaultConverter(JsonSerializerOptions options, DefaultConverterFactory<T> factory)
{
this.factory = factory;
this.modifiedOptions = options.CopyAndRemoveConverter(factory.GetType());
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => factory.Write(writer, value, modifiedOptions);
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => factory.Read(ref reader, typeToConvert, modifiedOptions);
}
protected virtual T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions)
=> (T)JsonSerializer.Deserialize(ref reader, typeToConvert, modifiedOptions);
protected virtual void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions modifiedOptions)
=> JsonSerializer.Serialize(writer, value, modifiedOptions);
public override bool CanConvert(Type typeToConvert) => typeof(T) == typeToConvert;
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new DefaultConverter(options, this);
}
public static class JsonSerializerExtensions
{
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;
}
}
然后,在从 DefaultConverterFactory<T>
派生的任何 类 中,例如 this one here,从 Read()
和 Write()
中删除最终参数 JsonConverter<T> defaultConverter
,您的代码现在应该可以工作了。
(顺便说一句,您似乎对模型使用了 [JsonSerializable(typeof(Link))]
wrongly. You are applying it to your model class Link
but, according to the docs, it should be applied to some subclass of JsonSerializerContext
-- 而不是模型本身。)
我正在使用 C# 实现 ActivityPub 实现,有时 link 是“字符串”,例如 url link,有时 link 是具有 Link 子类型的对象。 (Link:实体)
我想知道如果一组特定条件为真,是否有可能使用 System.Text.Json 将 Link 对象序列化为字符串(只需将一个字符串写入作者) , 如果条件不成立,则将整个默认对象写入 writer。
我已尝试遵循此解决方案:
有谁知道我该如何调试它,或者有更好的方法来确保 Link : Entity
对象有时可以序列化为字符串?
我得到以下错误:
(在这种情况下,我什至尝试将获取的默认 ctor 添加到 modifiedOptions) 大意,它说 Link class 没有数据映射。 我还尝试将 JsonSerializeable 属性直接添加到 Link class.
错误:
Metadata for type 'ActivityPub.Types.Link' was not provided to the serializer. The serializer method used does not support reflection-based creation of serialization-related type metadata. If using source generation, ensure that all root types passed to the serializer have been indicated with 'JsonSerializableAttribute', along with any types that might be serialized polymorphically.
我的基础代码库:https://github.com/Meep-Tech/ActivityHub.Net/tree/collapse_links_during_serialization
测试代码:
static void Main(string[] args) {
Settings.DefaultContext = new Link("ActivityPub.Net.Testing");
var testObject = new Object {
Type = "Test",
At = new Link("/terry") {
Rels = new string[] {
"test",
"test2"
}
},
Attribution = "/meep",
Audience = new Link("/all") {
Rel = "test"
}
};
string json = testObject
.Serialize();
System.IO.File.WriteAllLines(
"test.json",
new[] { json }
);
Object @object = json.DeSerializeEntity<Object>();
System.IO.File.WriteAllLines(
"test1.json",
new[] { @object.ToString() }
);
}
在我的 original version of DefaultConverterFactory<T>
, I cached the default converter because, in its documentation How to write custom converters for JSON serialization (marshalling) in .NET 中,Microsoft 建议在序列化复杂对象时出于性能原因缓存任何所需的转换器:
public DictionaryEnumConverterInner(JsonSerializerOptions options) { // For performance, use the existing converter if available. _valueConverter = (JsonConverter<TValue>)options .GetConverter(typeof(TValue)); // Cache the key and value types. _keyType = typeof(TKey); _valueType = typeof(TValue); }
但是,由于以下几个原因,这已被证明是有问题的:
当使用 声明的 类型
object
序列化多态值时,GetConverter()
返回一个非功能转换器。序列化数值时,返回的转换器忽略
NumberHandling
设置。现在看来您可能遇到了第三个问题:使用compile-time serializer source generation时,返回的转换器可能无法工作。
有足够的问题可以忽略 Microsoft 的建议。只需DefaultConverter<T>
如下:
public abstract class DefaultConverterFactory<T> : JsonConverterFactory
{
class DefaultConverter : JsonConverter<T>
{
readonly JsonSerializerOptions modifiedOptions;
readonly DefaultConverterFactory<T> factory;
public DefaultConverter(JsonSerializerOptions options, DefaultConverterFactory<T> factory)
{
this.factory = factory;
this.modifiedOptions = options.CopyAndRemoveConverter(factory.GetType());
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => factory.Write(writer, value, modifiedOptions);
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => factory.Read(ref reader, typeToConvert, modifiedOptions);
}
protected virtual T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions)
=> (T)JsonSerializer.Deserialize(ref reader, typeToConvert, modifiedOptions);
protected virtual void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions modifiedOptions)
=> JsonSerializer.Serialize(writer, value, modifiedOptions);
public override bool CanConvert(Type typeToConvert) => typeof(T) == typeToConvert;
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new DefaultConverter(options, this);
}
public static class JsonSerializerExtensions
{
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;
}
}
然后,在从 DefaultConverterFactory<T>
派生的任何 类 中,例如 this one here,从 Read()
和 Write()
中删除最终参数 JsonConverter<T> defaultConverter
,您的代码现在应该可以工作了。
(顺便说一句,您似乎对模型使用了 [JsonSerializable(typeof(Link))]
wrongly. You are applying it to your model class Link
but, according to the docs, it should be applied to some subclass of JsonSerializerContext
-- 而不是模型本身。)