11

我只是尝试使用 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 对象反序列化。

我怎样才能让它工作?

4

5 回答 5

19

在 BsonReader 上将 ReadRootValueAsArray 设置为 true

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

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

于 2013-06-05T21:00:28.633 回答
6

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

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

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

希望这可以帮助。

于 2013-06-04T07:57:39.123 回答
1

通常,您可以在将 ReadRootValueAsArray 设置为 true 之前先检查数据类型,如下所示:

if (typeof(IEnumerable).IsAssignableFrom(type)) 
    bSonReader.ReadRootValueAsArray = true;
于 2016-02-28T16:46:48.393 回答
1

正如James Newton-King在这个答案中所解释的那样,BSON 格式不会保存有关根值是否为集合的元数据,因此有必要在开始反序列化之前进行适当设置。BsonDataReader.ReadRootValueAsArray

当反序列化为某些已知的POCO类型(而不是dynamicor JToken)时,一种简单的方法是根据根类型是否将使用数组协定进行序列化来初始化阅读器。以下扩展方法执行此操作:

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

笔记:

  • Newtonsoft.Json.BsonBSON 支持已移至Json.NET 10.0.1中自己的包. 在此版本和更高版本中,BsonDataReader替换了 now-obsolete BsonReader

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

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

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

演示小提琴在这里

于 2020-01-15T20:22:25.277 回答
1

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

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

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

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

于 2017-05-11T15:22:33.197 回答