为啥使用 Json.NET 序列化为 json 字符串时缺少 DriveInfo 的属性?

Posted

技术标签:

【中文标题】为啥使用 Json.NET 序列化为 json 字符串时缺少 DriveInfo 的属性?【英文标题】:Why are DriveInfo's properties missing when serializing to json string using Json.NET?为什么使用 Json.NET 序列化为 json 字符串时缺少 DriveInfo 的属性? 【发布时间】:2016-09-07 06:57:44 【问题描述】:

尝试使用此代码将 DrivInfo 序列化为 Json 字符串仅返回“名称”属性:

DriveInfo dr = new DriveInfo("C");    
string json = Newtonsoft.Json.JsonConvert.SerializeObject(dr);

字符串结果只有: "_name":"C:\"

DrivInfo 是密封的,所以我无法更改任何内容。有没有办法不包装?

【问题讨论】:

为什么要序列化你无法控制的类型?如果 MS 决定更改 DriveInfo 的实现怎么办? 为了在公司内部使用,我编写了一个程序来转储环境信息。所以对象被序列化然后反序列化为 xml 文件。由于 XmlSerializer 与默认构造函数的限制,我使用序列化的 json 字符串。 【参考方案1】:

你的难点在于DriveInfo实现了ISerializable接口用于自定义序列化,而Json.NET默认尊重这个接口,使用它来序列化和反序列化类型。由于DriveInfo 完全由驱动器的名称定义,这就是它的自定义序列化代码存储到序列化流中的全部内容。

由于您只想转储DriveInfo 的属性而不关心反序列化,您可以通过设置DefaultContractResolver.IgnoreSerializableInterface = true 来禁用ISerializable。然而,如果你这样做,你会得到一个无限递归序列化dr.RootDirectory.Root.Root...。要解决此问题,您可以为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);

(当然,你不能以这种格式反序列化它。)

【讨论】:

【参考方案2】:

该类包含它自己的自定义序列化,它指定只应包含_name 字段。其他属性不存储在类中。它们是由环境决定的,因此不能用反序列化的值替换。

来自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 引用的驱动器来确定的。 TotalFreeSpaceTotalFreeSize 之类的属性可以随时更改,并且可能不会(可能不会)应用于可以反序列化该类的另一台计算机上。

如果您可以序列化整个事物并在其他地方反序列化,那么就有可能创建一个所有属性值都错误的类的反序列化实例,因为例如,他们实际上描述了另一台计算机上的c: 驱动器。但是该类的目的是返回有关执行代码的计算机上的驱动器的信息。

如果您想传递这些数据,那么您可以随时创建自己的类并对其进行序列化。

【讨论】:

谢谢,这解释了一切。所以包装器是我唯一的方法。

以上是关于为啥使用 Json.NET 序列化为 json 字符串时缺少 DriveInfo 的属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Json.NET 将 XML 序列化为 JSON 对象

JSON.Net 将集合序列化为数组数组

如何将字典的值序列化为json(使用json.net)[重复]

如何使用 NewtonSoft Json.Net 将 Json 字典反序列化为平面类

csharp 使用Newtonsoft JSON.NET将任何对象序列化/反序列化为JSON

IEnumerable 在 Json.NET 中序列化为 [JsonObject],不使用属性