See the question and my original answer on StackOverflow

The problem when using JObject.ToString, BsonDocument.Parse, etc. is the performance is not very good because you do the same operations multiple times, you do string allocations, parsing, etc.

So, I have written a function that converts a JObject to an IEnumerable<KeyValuePair<string, object>> (only using enumerations), which is a type usable by one of the BsonDocument constructors. Here is the code:

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