为什么在使用 Json.NET 序列化为 json 字符串时缺少 DriveInfo 的属性?
Why are DriveInfo's properties missing when serializing to json string using Json.NET?
正在尝试仅使用此代码将 DrivInfo 序列化为 Json 字符串 return "name" 属性:
DriveInfo dr = new DriveInfo("C");
string json = Newtonsoft.Json.JsonConvert.SerializeObject(dr);
字符串结果只有:
{"_name":"C:\"}
DrivInfo 是密封的,所以我无法更改任何内容。有没有办法做到这一点不包括包装?
class 包含其自己的自定义序列化,指定仅应包含 _name
字段。其他属性未存储在 class 中。它们是由环境决定的,所以不能用反序列化的值代替。
来自source code:
private const String NameField = "_name"; // For serialization
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
// No need for an additional security check - everything is public.
info.AddValue(NameField, _name, typeof(String));
}
其他属性通过实际检查 DriveInfo
引用的驱动器来确定。 TotalFreeSpace
和 TotalFreeSize
之类的属性可能会随时更改,并且可能不会(可能不会)应用于可以反序列化 class 的另一台计算机。
如果你可以序列化整个东西并在其他地方反序列化它,那么就有可能创建一个class的反序列化实例,其中所有属性 值是错误的,因为,例如,它们实际上描述了另一台计算机上的 c:
驱动器。但是 class 的目的是 return 有关执行代码的计算机上驱动器的信息。
如果您想传递该数据,那么您可以随时创建自己的 class 并将其序列化。
你的难点是DriveInfo
implements the ISerializable
自定义序列化的接口,而Json.NET默认尊重这个接口,用它来序列化和反序列化类型。由于 DriveInfo
完全由驱动器的名称定义,这就是它的自定义序列化代码存储到序列化流中的所有内容。
因为你只想转储DriveInfo
的属性而不关心反序列化,你可以通过设置DefaultContractResolver.IgnoreSerializableInterface = true
. However, if you do this, you'll get an infinite recursion serializing dr.RootDirectory.Root.Root...
禁用ISerializable
。要解决此问题,您可以为 DirectoryInfo
:
创建一个 JsonConverter
public class DirectoryInfoConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DirectoryInfo);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var token = JToken.Load(reader);
return new DirectoryInfo((string)token);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
}
那么你可以这样做:
DriveInfo dr = new DriveInfo("C");
var settings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver { IgnoreSerializableInterface = true },
Converters = new [] { new DirectoryInfoConverter() },
};
var json = Newtonsoft.Json.JsonConvert.SerializeObject(dr, Formatting.Indented, settings);
但此时,序列化中间匿名类型可能更容易:
var json = JsonConvert.SerializeObject(
new
{
Name = dr.Name,
DriveType = dr.DriveType,
DriveFormat = dr.DriveFormat,
IsReady = dr.IsReady,
AvailableFreeSpace = dr.AvailableFreeSpace,
TotalFreeSpace = dr.TotalFreeSpace,
TotalSize = dr.TotalSize,
RootDirectory = dr.RootDirectory.ToString(),
VolumeLabel = dr.VolumeLabel
},
Formatting.Indented);
(当然,这种格式是不能反序列化的)
正在尝试仅使用此代码将 DrivInfo 序列化为 Json 字符串 return "name" 属性:
DriveInfo dr = new DriveInfo("C");
string json = Newtonsoft.Json.JsonConvert.SerializeObject(dr);
字符串结果只有:
{"_name":"C:\"}
DrivInfo 是密封的,所以我无法更改任何内容。有没有办法做到这一点不包括包装?
class 包含其自己的自定义序列化,指定仅应包含 _name
字段。其他属性未存储在 class 中。它们是由环境决定的,所以不能用反序列化的值代替。
来自source code:
private const String NameField = "_name"; // For serialization
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
// No need for an additional security check - everything is public.
info.AddValue(NameField, _name, typeof(String));
}
其他属性通过实际检查 DriveInfo
引用的驱动器来确定。 TotalFreeSpace
和 TotalFreeSize
之类的属性可能会随时更改,并且可能不会(可能不会)应用于可以反序列化 class 的另一台计算机。
如果你可以序列化整个东西并在其他地方反序列化它,那么就有可能创建一个class的反序列化实例,其中所有属性 值是错误的,因为,例如,它们实际上描述了另一台计算机上的 c:
驱动器。但是 class 的目的是 return 有关执行代码的计算机上驱动器的信息。
如果您想传递该数据,那么您可以随时创建自己的 class 并将其序列化。
你的难点是DriveInfo
implements the ISerializable
自定义序列化的接口,而Json.NET默认尊重这个接口,用它来序列化和反序列化类型。由于 DriveInfo
完全由驱动器的名称定义,这就是它的自定义序列化代码存储到序列化流中的所有内容。
因为你只想转储DriveInfo
的属性而不关心反序列化,你可以通过设置DefaultContractResolver.IgnoreSerializableInterface = true
. However, if you do this, you'll get an infinite recursion serializing dr.RootDirectory.Root.Root...
禁用ISerializable
。要解决此问题,您可以为 DirectoryInfo
:
JsonConverter
public class DirectoryInfoConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DirectoryInfo);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var token = JToken.Load(reader);
return new DirectoryInfo((string)token);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
}
那么你可以这样做:
DriveInfo dr = new DriveInfo("C");
var settings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver { IgnoreSerializableInterface = true },
Converters = new [] { new DirectoryInfoConverter() },
};
var json = Newtonsoft.Json.JsonConvert.SerializeObject(dr, Formatting.Indented, settings);
但此时,序列化中间匿名类型可能更容易:
var json = JsonConvert.SerializeObject(
new
{
Name = dr.Name,
DriveType = dr.DriveType,
DriveFormat = dr.DriveFormat,
IsReady = dr.IsReady,
AvailableFreeSpace = dr.AvailableFreeSpace,
TotalFreeSpace = dr.TotalFreeSpace,
TotalSize = dr.TotalSize,
RootDirectory = dr.RootDirectory.ToString(),
VolumeLabel = dr.VolumeLabel
},
Formatting.Indented);
(当然,这种格式是不能反序列化的)