我在反序列化枚举时遇到问题
I'm having problems deserializing enumerations
我正在使用 NLog,并且正在通过网络发送一个 LogEventInfo
对象,我不拥有这个对象,因此我不能用 [JsonConverter(typeof(StringEnumConverter))]
装饰它,或者我可以并且我只是不知道如何
我试过以下代码,但没有用
var strategy = new Newtonsoft.Json.Serialization.CamelCaseNamingStrategy();
var sec = new StringEnumConverter(strategy, false);
//TEMP Hard code serialization and message routing for just logs
LogEventInfo info = Newtonsoft.Json.JsonConvert.DeserializeObject<LogEventInfo>(msg.Body, sec);
有没有人有幸获得枚举反序列化并且枚举在 class 你不拥有的地方?
消息如下:
{
"date":"2019-06-04 21:48:24.0753",
"level":"Error",
"message":"{\"ApplicationId\":1390760,\"AppStatus\":\"#PG2\",\"Status\":400,\"ErrorCode\": 1053 }",
"properties":"ResponseBody={\"ApplicationId\":1390760,\"AppStatus\":\"#PG2\",\"Status\":400,\"ErrorCode\": 1053 }|GroupId=392934|ApplicationId=1390760|Status=400",
"callsite":"HFD.Enterprise.Logging.Tests.LogTest.LoadTest",
"logger":"WebApiLog",
"machinename":"BRANDONHOSTVS"
}
这里是个例外:
Newtonsoft.Json.JsonSerializationException: Error converting value "Error" to type 'NLog.LogLevel'. Path 'level', line 1, position 72. ---> System.InvalidCastException: Invalid cast from 'System.String' to 'NLog.LogLevel'.
at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)
at System.String.System.IConvertible.ToType(Type type, IFormatProvider provider)
at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType)
--- End of inner exception stack trace ---
你的基本问题是 LogEventInfo.Level
is not an enum, it is a class LogLevel
恰好实现了 IConvertible
并且有一组全局静态标准值:
public sealed class LogLevel : IComparable, IEquatable<LogLevel>, IConvertible
{
/// <summary>
/// Gets all the available log levels (Trace, Debug, Info, Warn, Error, Fatal, Off).
/// </summary>
public static IEnumerable<LogLevel> AllLevels => allLevels;
添加 StringEnumConverter
不会帮助您(反)序列化这样的 属性。
此外,NLog 似乎没有为 LogLevel
提供自定义 TypeConverter
。如果我调用 TypeDescriptor.GetConverter(typeof(LogLevel))
,返回值是默认转换器 System.ComponentModel.TypeConverter
的实例,这意味着 Json.NET 无法将序列化的 LogLevel
字符串值转换回 LogLevel
实例.
尽管如此,您的 JSON 样本确实将 Level
表示为 "level": "Error"
,因此发送系统必须使用某种 custom JsonConverter
来表示 LogLevel
仅序列化 LogLevel.Name
。我们可以很容易地重新创建这样一个 JsonConverter
,如下所示:
public class LogLevelConverter : JsonConverter<LogLevel>
{
public override LogLevel ReadJson(JsonReader reader, Type objectType, LogLevel existingValue, bool hasExistingValue, JsonSerializer serializer)
{
switch (reader.MoveToContentAndAssert().TokenType)
{
case JsonToken.Null:
return null;
case JsonToken.String:
return LogLevel.FromString((string)reader.Value);
default:
throw new JsonSerializationException(string.Format("Unknown token {0}", reader.TokenType));
}
}
public override void WriteJson(JsonWriter writer, LogLevel value, JsonSerializer serializer)
{
var logLevel = (LogLevel)value;
writer.WriteValue(logLevel.Name);
}
}
public static partial class JsonExtensions
{
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None) // Skip past beginning of stream.
reader.ReadAndAssert();
while (reader.TokenType == JsonToken.Comment) // Skip past comments.
reader.ReadAndAssert();
return reader;
}
public static JsonReader ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
throw new JsonReaderException("Unexpected end of JSON stream.");
return reader;
}
}
然后使用如下:
var seq = new LogLevelConverter();
var info = Newtonsoft.Json.JsonConvert.DeserializeObject<LogEventInfo>(msg.Body, seq);
演示 fiddle here.
我正在使用 NLog,并且正在通过网络发送一个 LogEventInfo
对象,我不拥有这个对象,因此我不能用 [JsonConverter(typeof(StringEnumConverter))]
装饰它,或者我可以并且我只是不知道如何
我试过以下代码,但没有用
var strategy = new Newtonsoft.Json.Serialization.CamelCaseNamingStrategy();
var sec = new StringEnumConverter(strategy, false);
//TEMP Hard code serialization and message routing for just logs
LogEventInfo info = Newtonsoft.Json.JsonConvert.DeserializeObject<LogEventInfo>(msg.Body, sec);
有没有人有幸获得枚举反序列化并且枚举在 class 你不拥有的地方?
消息如下:
{
"date":"2019-06-04 21:48:24.0753",
"level":"Error",
"message":"{\"ApplicationId\":1390760,\"AppStatus\":\"#PG2\",\"Status\":400,\"ErrorCode\": 1053 }",
"properties":"ResponseBody={\"ApplicationId\":1390760,\"AppStatus\":\"#PG2\",\"Status\":400,\"ErrorCode\": 1053 }|GroupId=392934|ApplicationId=1390760|Status=400",
"callsite":"HFD.Enterprise.Logging.Tests.LogTest.LoadTest",
"logger":"WebApiLog",
"machinename":"BRANDONHOSTVS"
}
这里是个例外:
Newtonsoft.Json.JsonSerializationException: Error converting value "Error" to type 'NLog.LogLevel'. Path 'level', line 1, position 72. ---> System.InvalidCastException: Invalid cast from 'System.String' to 'NLog.LogLevel'.
at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)
at System.String.System.IConvertible.ToType(Type type, IFormatProvider provider)
at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType)
--- End of inner exception stack trace ---
你的基本问题是 LogEventInfo.Level
is not an enum, it is a class LogLevel
恰好实现了 IConvertible
并且有一组全局静态标准值:
public sealed class LogLevel : IComparable, IEquatable<LogLevel>, IConvertible
{
/// <summary>
/// Gets all the available log levels (Trace, Debug, Info, Warn, Error, Fatal, Off).
/// </summary>
public static IEnumerable<LogLevel> AllLevels => allLevels;
添加 StringEnumConverter
不会帮助您(反)序列化这样的 属性。
此外,NLog 似乎没有为 LogLevel
提供自定义 TypeConverter
。如果我调用 TypeDescriptor.GetConverter(typeof(LogLevel))
,返回值是默认转换器 System.ComponentModel.TypeConverter
的实例,这意味着 Json.NET 无法将序列化的 LogLevel
字符串值转换回 LogLevel
实例.
尽管如此,您的 JSON 样本确实将 Level
表示为 "level": "Error"
,因此发送系统必须使用某种 custom JsonConverter
来表示 LogLevel
仅序列化 LogLevel.Name
。我们可以很容易地重新创建这样一个 JsonConverter
,如下所示:
public class LogLevelConverter : JsonConverter<LogLevel>
{
public override LogLevel ReadJson(JsonReader reader, Type objectType, LogLevel existingValue, bool hasExistingValue, JsonSerializer serializer)
{
switch (reader.MoveToContentAndAssert().TokenType)
{
case JsonToken.Null:
return null;
case JsonToken.String:
return LogLevel.FromString((string)reader.Value);
default:
throw new JsonSerializationException(string.Format("Unknown token {0}", reader.TokenType));
}
}
public override void WriteJson(JsonWriter writer, LogLevel value, JsonSerializer serializer)
{
var logLevel = (LogLevel)value;
writer.WriteValue(logLevel.Name);
}
}
public static partial class JsonExtensions
{
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None) // Skip past beginning of stream.
reader.ReadAndAssert();
while (reader.TokenType == JsonToken.Comment) // Skip past comments.
reader.ReadAndAssert();
return reader;
}
public static JsonReader ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
throw new JsonReaderException("Unexpected end of JSON stream.");
return reader;
}
}
然后使用如下:
var seq = new LogLevelConverter();
var info = Newtonsoft.Json.JsonConvert.DeserializeObject<LogEventInfo>(msg.Body, seq);
演示 fiddle here.