为什么在使用 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 引用的驱动器来确定。 TotalFreeSpaceTotalFreeSize 之类的属性可能会随时更改,并且可能不会(可能不会)应用于可以反序列化 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);

(当然,这种格式是不能反序列化的)