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

时间:2016-05-11 17:53:26

标签: c# json serialization json.net jsonserializer

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

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

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

DrivInfo是密封的,所以我无法改变任何东西。有没有办法排除包装?

2 个答案:

答案 0 :(得分:1)

您的难点在于DriveInfo实现了自定义序列化的ISerializable接口,默认情况下Json.NET尊重此接口,使用它来序列化和反序列化类型。由于DriveInfo完全由驱动器名称定义,因此它的所有自定义序列化代码都存储在序列化流中。

由于您只想转储DriveInfo的属性而不关心反序列化,因此您可以通过设置DefaultContractResolver.IgnoreSerializableInterface = true来停用ISerializable。但是,如果这样做,您将获得序列化dr.RootDirectory.Root.Root...的无限递归。要解决此问题,您可以为JsonConverter创建DirectoryInfo

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);

(当然,您无法以此格式反序列化它。)

答案 1 :(得分:0)

该类包含自己的自定义序列化,指定只应包含_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:驱动器。但是,该类的目的是返回有关执行代码的计算机上的驱动器的信息。

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