【问题标题】:Bson array (de)serialization with Json.NET使用 Json.NET 进行 Bson 数组(反)序列化
【发布时间】:2020-01-30 21:08:26
【问题描述】:

我只是尝试使用 Json.NET 对 Bson 格式的字符串数组进行序列化和反序列化,但以下代码失败:

var jsonSerializer = new JsonSerializer();
var array = new string [] { "A", "B" };

// Serialization
byte[] bytes;
using (var ms = new MemoryStream())
using (var bson = new BsonWriter(ms))
{
    jsonSerializer.Serialize(bson, array, typeof(string[]));
    bytes = ms.ToArray();
}

// Deserialization
using (var ms = new MemoryStream(bytes))
using (var bson = new BsonReader(ms))
{
    // Exception here
    array = jsonSerializer.Deserialize<string[]>(bson);
}

异常信息:

无法将当前 JSON 对象(例如 {"name":"value"})反序列化为类型“System.String[]”,因为该类型需要 JSON 数组(例如 [1,2,3])才能正确反序列化.

要修复此错误,要么将 JSON 更改为 JSON 数组(例如 [1,2,3]),要么将反序列化类型更改为正常的 .NET 类型(例如,不是像整数这样的原始类型,而不是可以从 JSON 对象反序列化的集合类型(如数组或列表)。也可以将 JsonObjectAttribute 添加到类型中以强制它从 JSON 对象反序列化。

我怎样才能让它工作?

【问题讨论】:

    标签: json.net bson


    【解决方案1】:

    嗯,从我坐的位置来看,您的代码应该可以工作,但 Json.Net 似乎认为您的序列化字符串数组是一个字典。这可能是因为,根据BSON specification,数组实际上确实像对象一样被序列化为键值对列表。在这种情况下,键只是数组索引值的字符串表示形式。

    无论如何,我可以通过几种不同的方式解决这个问题:

    1. 反序列化为字典,然后手动将其转换回数组。

      var jsonSerializer = new JsonSerializer();
      var array = new string[] { "A", "B" };
      
      // Serialization
      byte[] bytes;
      using (var ms = new MemoryStream())
      using (var bson = new BsonWriter(ms))
      {
          jsonSerializer.Serialize(bson, array);
          bytes = ms.ToArray();
      }
      
      // Deserialization
      using (var ms = new MemoryStream(bytes))
      using (var bson = new BsonReader(ms))
      {
          var dict = jsonSerializer.Deserialize<Dictionary<string, string>>(bson);
          array = dict.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value).ToArray();
      }
      
    2. 将数组包装在外部对象中。

      class Wrapper
      {
          public string[] Array { get; set; }
      }
      

      然后使用包装对象进行序列化和反序列化。

      var jsonSerializer = new JsonSerializer();
      var obj = new Wrapper { Array = new string[] { "A", "B" } };
      
      // Serialization
      byte[] bytes;
      using (var ms = new MemoryStream())
      using (var bson = new BsonWriter(ms))
      {
          jsonSerializer.Serialize(bson, obj);
          bytes = ms.ToArray();
      }
      
      // Deserialization
      using (var ms = new MemoryStream(bytes))
      using (var bson = new BsonReader(ms))
      {
          obj = jsonSerializer.Deserialize<Wrapper>(bson);
      }
      

    希望这会有所帮助。

    【讨论】:

    • 感谢分析和可能的解决方法。在我的情况下,虽然它们有点难以实现,因为我需要这段代码来处理未知类型。我在 CodePlex 网站上打开了一个问题:json.codeplex.com/workitem/24365
    • @XavierPoinas 第二种解决方案,将数组包装在外部对象中,非常适用于未知类型。只需更改包装类的属性类型,即public object Value { get; set; } 而不是public string[] Array { get; set; },您也可以将其与任何非数组类型一起使用。创建一个自动包装/解包的帮助类很容易。
    【解决方案2】:

    在 BsonReader 上将 ReadRootValueAsArray 设置为 true

    http://james.newtonking.com/projects/json/help/index.html?topic=html/P_Newtonsoft_Json_Bson_BsonReader_ReadRootValueAsArray.htm

    此设置是必需的,因为 BSON 数据规范不保存有关根值是对象还是数组的元数据。

    【讨论】:

      【解决方案3】:

      一般情况下,可以先检查数据类型,然后再将 ReadRootValueAsArray 设置为 true,如下所示:

      if (typeof(IEnumerable).IsAssignableFrom(type)) 
          bSonReader.ReadRootValueAsArray = true;
      

      【讨论】:

        【解决方案4】:

        我知道这是一个旧线程,但我在使用 MongoDB.Driver 的强大功能时发现了一个简单的反序列化

        您可以使用 BsonDocument.parse(JSONString) 反序列化 JSON 对象,以便反序列化字符串数组,请使用:

        string Jsonarray = "[\"value1\", \"value2\", \"value3\"]";
        BsonArray deserializedArray = BsonDocument.parse("{\"arr\":" + Jsonarray + "}")["arr"].asBsonArray;
        

        deserializedArray 然后可以用作任何数组,例如 foreach 循环。

        【讨论】:

          【解决方案5】:

          正如James Newton-Kingthis answer 中解释的那样,BSON 格式不保存有关根值是否为集合的元数据,因此有必要在开始反序列化之前适当地设置BsonDataReader.ReadRootValueAsArray

          当反序列化为某些已知的POCO 类型(而不是dynamicJToken)时,一种简单的方法是根据根类型是否将使用array contract 序列化来初始化阅读器.以下扩展方法可以做到这一点:

          public static partial class BsonExtensions
          {
              public static T DeserializeFromFile<T>(string path, JsonSerializerSettings settings = null)
              {
                  using (var stream = new FileStream(path, FileMode.Open))
                      return Deserialize<T>(stream, settings);
              }
          
              public static T Deserialize<T>(byte [] data, JsonSerializerSettings settings = null)
              {
                  using (var stream = new MemoryStream(data))
                      return Deserialize<T>(stream, settings);
              }
          
              public static T Deserialize<T>(byte [] data, int index, int count, JsonSerializerSettings settings = null)
              {
                  using (var stream = new MemoryStream(data, index, count))
                      return Deserialize<T>(stream, settings);
              }
          
              public static T Deserialize<T>(Stream stream, JsonSerializerSettings settings = null)
              {
                  // Use BsonReader in Json.NET 9 and earlier.
                  using (var reader = new BsonDataReader(stream) { CloseInput = false })  // Let caller dispose the stream
                  {
                      var serializer = JsonSerializer.CreateDefault(settings);
                      //https://www.newtonsoft.com/json/help/html/DeserializeFromBsonCollection.htm
                      if (serializer.ContractResolver.ResolveContract(typeof(T)) is JsonArrayContract)
                          reader.ReadRootValueAsArray = true;
                      return serializer.Deserialize<T>(reader);
                  }
              }
          }
          

          现在你可以简单地做:

          var newArray = BsonExtensions.Deserialize<string []>(bytes);
          

          注意事项:

          • BSON 支持已移至 Json.NET 10.0.1 中自己的包 Newtonsoft.Json.Bson。在此版本和更高版本中,BsonDataReader 替换了现已过时的BsonReader

          • 可以使用相同的扩展方法来反序列化字典,例如:

            var newDictionary = BsonExtensions.Deserialize<SortedDictionary<int, string>>(bytes);
            

            通过检查合同类型ReadRootValueAsArray 是否正确设置。

          演示小提琴here.

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多