14

我将原始 JSON 插入到集合中,发现数据库中存储的内容缺少值。例如,我的集合是一个集合BsonDocuments

_products = database.GetCollection<BsonDocument>("products");

将 JSON 插入集合的代码:

public int AddProductDetails(JObject json)
{
    var doc = json.ToBsonDocument(DictionarySerializationOptions.Document);
    _products.Insert(doc);
}

传入的 JSON 如下所示:

{
  "Id": 1,
  "Tags": [
    "book",
    "database"
  ],
  "Name": "Book Name",
  "Price": 12.12
}

但是,集合中保留的只是没有值的属性。

{
  "_id": {
    "$oid": "5165c7e10fdb8c09f446d720"
  },
  "Id": [],
  "Tags": [
    [],
    []
  ],
  "Name": [],
  "Price": []
}

为什么值被丢弃?

4

6 回答 6

19

这符合我的预期。

    public int AddProductDetails(JObject json)
    {
        BsonDocument doc = BsonDocument.Parse(json.ToString());
        _products.Insert(doc);
    }
于 2013-04-10T22:29:32.083 回答
12

当我有一个具有 type 属性的 C# 类时,我遇到了这个问题JObject

我的解决方案是JObjectSerializer为 MondoDB 创建并将属性添加到属性中,以便 Mongo 序列化程序使用它。我假设如果我足够努力,我也可以在 Mongo 中将以下序列化程序注册为这种类型的全局序列化程序。

注册序列化器进行属性处理:

[BsonSerializer(typeof(JObjectSerializer))]
public JObject AdditionalData { get; set; }

序列化程序本身:

public class JObjectSerializer : SerializerBase<JObject> // IBsonSerializer<JObject>
{
    public override JObject Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
    {
        var myBSONDoc = BsonDocumentSerializer.Instance.Deserialize(context);
        return JObject.Parse(myBSONDoc.ToString());
    }

    public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, JObject value)
    {
        var myBSONDoc = MongoDB.Bson.BsonDocument.Parse(value.ToString());
        BsonDocumentSerializer.Instance.Serialize(context, myBSONDoc);
    }
}
于 2017-04-30T13:58:33.720 回答
7

使用JObject.ToString,BsonDocument.Parse等时的问题是性能不是很好,因为您多次执行相同的操作,您执行字符串分配、解析等。

因此,我编写了一个将 a 转换JObject为 an IEnumerable<KeyValuePair<string, object>>(仅使用枚举)的函数,它是一种可由BsonDocument构造函数之一使用的类型。这是代码:

public static BsonDocument ToBsonDocument(this JObject jo)
{
    if (jo == null)
        return null;

    return new BsonDocument(ToEnumerableWithObjects(jo));
}

public static IEnumerable<KeyValuePair<string, object>> ToEnumerableWithObjects(this JObject jo)
{
    if (jo == null)
        return Enumerable.Empty<KeyValuePair<string, object>>();

    return new JObjectWrapper(jo);
}

private class JObjectWrapper : IEnumerable<KeyValuePair<string, object>>
{
    private JObject _jo;

    public JObjectWrapper(JObject jo)
    {
        _jo = jo;
    }

    public IEnumerator<KeyValuePair<string, object>> GetEnumerator() => new JObjectWrapperEnumerator(_jo);
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    public static object ToValue(JToken token)
    {
        object value;
        switch (token.Type)
        {
            case JTokenType.Object:
                value = new JObjectWrapper((JObject)token);
                break;

            case JTokenType.Array:
                value = new JArrayWrapper((JArray)token);
                break;

            default:
                if (token is JValue jv)
                {
                    value = ((JValue)token).Value;
                }
                else
                {
                    value = token.ToString();
                }
                break;
        }
        return value;
    }
}

private class JArrayWrapper : IEnumerable
{
    private JArray _ja;

    public JArrayWrapper(JArray ja)
    {
        _ja = ja;
    }

    public IEnumerator GetEnumerator() => new JArrayWrapperEnumerator(_ja);
}

private class JArrayWrapperEnumerator : IEnumerator
{
    private IEnumerator<JToken> _enum;

    public JArrayWrapperEnumerator(JArray ja)
    {
        _enum = ja.GetEnumerator();
    }

    public object Current => JObjectWrapper.ToValue(_enum.Current);
    public bool MoveNext() => _enum.MoveNext();
    public void Reset() => _enum.Reset();
}

private class JObjectWrapperEnumerator : IEnumerator<KeyValuePair<string, object>>
{
    private IEnumerator<KeyValuePair<string, JToken>> _enum;

    public JObjectWrapperEnumerator(JObject jo)
    {
        _enum = jo.GetEnumerator();
    }

    public KeyValuePair<string, object> Current => new KeyValuePair<string, object>(_enum.Current.Key, JObjectWrapper.ToValue(_enum.Current.Value));
    public bool MoveNext() => _enum.MoveNext();
    public void Dispose() => _enum.Dispose();
    public void Reset() => _enum.Reset();
    object IEnumerator.Current => Current;
}
于 2018-09-21T17:47:31.723 回答
3

您是否尝试过使用 BsonSerializer?

using MongoDB.Bson.Serialization;
[...]
var document = BsonSerializer.Deserialize<BsonDocument>(json);

BsonSerializer 适用于字符串,因此如果 JSON 参数是 JObject(或 JArray、JRaw 等),则必须使用 JsonConvert.SerializeObject() 对其进行序列化

于 2013-04-10T20:57:15.380 回答
0

这是 Andrew DeVries答案的更新版本,其中包括处理序列化/反序列化空值。

public class JObjectSerializer : SerializerBase<JObject>
{
    public override JObject Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
    {
        if (context.Reader.CurrentBsonType != BsonType.Null)
        {
            var myBSONDoc = BsonDocumentSerializer.Instance.Deserialize(context);
            return JObject.Parse(myBSONDoc.ToStrictJson());
        }
        else
        {
            context.Reader.ReadNull();
            return null;
        }
    }

    public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, JObject value)
    {
        if (value != null)
        {
            var myBSONDoc = BsonDocument.Parse(value.ToString());
            BsonDocumentSerializer.Instance.Serialize(context, myBSONDoc);
        }
        else
        {
            context.Writer.WriteNull();
        }
    }
}

ToStrictJson()调用是一种扩展方法,它将对内置 BSONToJson()方法的调用包装起来,包括将输出模式设置为严格。如果不这样做,解析将失败,因为 BSON 类型构造函数将保留在 JSON 输出中(ObjectId()例如 )。

这里也是实现ToStrictJson()

public static class MongoExtensionMethods
{
    /// <summary>
    /// Create a JsonWriterSettings object to use when serializing BSON docs to JSON.
    /// This will force the serializer to create valid ("strict") JSON.
    /// Without this, Object IDs and Dates are ouput as {"_id": ObjectId(ds8f7s9d87f89sd9f8d9f7sd9f9s8d)}
    ///  and {"date": ISODate("2020-04-14 14:30:00:000")} respectively, which is not valid JSON
    /// </summary>
    private static JsonWriterSettings jsonWriterSettings = new JsonWriterSettings()
    {
        OutputMode = JsonOutputMode.Strict
    };

    /// <summary>
    /// Custom extension method to convert MongoDB objects to JSON using the OutputMode = Strict setting.
    /// This ensure that the resulting string is valid JSON.
    /// </summary>
    /// <typeparam name="TNominalType">The type of object to convert to JSON</typeparam>
    /// <param name="obj">The object to conver to JSON</param>
    /// <returns>A strict JSON string representation of obj.</returns>
    public static string ToStrictJson<TNominalType>(this TNominalType obj)
    {
        return BsonExtensionMethods.ToJson<TNominalType>(obj, jsonWriterSettings);
    }
}
于 2020-05-01T13:50:34.280 回答
0

我使用以下。它基于西蒙的回答,感谢您的想法,并以相同的方式工作,避免不必要的序列化/反序列化为字符串。

由于 Linq 和 C# 10,它变得更加紧凑:

public static BsonDocument ToBsonDocument(this JObject o) =>
    new(o.Properties().Select(p => new BsonElement(p.Name, p.Value.ToBsonValue())));

public static BsonValue ToBsonValue(this JToken t) =>
    t switch
    {
        JObject o => o.ToBsonDocument(),
        JArray a => new BsonArray(a.Select(ToBsonValue)),
        JValue v => BsonValue.Create(v.Value),
        _ => throw new NotSupportedException($"ToBsonValue: {t}")
    };
于 2022-02-17T08:52:56.980 回答