为什么在使用 Json.NET 序列化为 json 字符串时缺少 Driveinfo 的属性?



尝试使用此代码将DrivInfo序列化为Json字符串只返回"name"属性:

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

字符串结果仅为:{"_name":"C:"}

DrivInfo已密封,因此我无法更改任何内容。有没有一种方法可以做到这一点,不包括包装?

您的困难在于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);

(当然,您将无法以这种格式对其进行反序列化。)

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

来自源代码:

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:驱动器。但该类的目的是返回有关执行代码的计算机上的驱动器的信息。

如果你想传递这些数据,那么你总是可以创建自己的类并序列化它。

相关内容

  • 没有找到相关文章

最新更新