代码之家  ›  专栏  ›  技术社区  ›  TheBugHunter

为什么使用json.NET序列化json字符串时DriveInfo的属性丢失?

  •  0
  • TheBugHunter  · 技术社区  · 8 年前

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

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

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

    DrivInfo已密封,因此我无法更改任何内容。有没有办法不包装?

    2 回复  |  直到 8 年前
        1
  •  2
  •   dbc    8 年前

    你的困难是 DriveInfo 实现 ISerializable 自定义序列化接口和Json。NET默认尊重此接口,使用它来序列化和反序列化类型。从那以后 驱动器信息 完全由驱动器的名称定义,这就是它的自定义序列化代码存储到序列化流中的全部内容。

    因为您只想转储 驱动器信息 并且不关心反序列化,您可以禁用 可序列化 通过设置 DefaultContractResolver.IgnoreSerializableInterface = true 然而,如果这样做,您将得到一个无限递归序列化 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);
    

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

        2
  •  0
  •   Scott Hannen    8 年前

    该类包含自己的自定义序列化,该序列化指定只有 _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 。属性如 TotalFreeSpace TotalFreeSize 可以随时更改,并且可能不会(可能不会)应用于可以反序列化该类的另一台计算机。

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

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