5

The following class shall be received by an API as Json and stored in MongoDB, using the C# Driver and Web API. The data property is unstructured, but I can restrict it to key-value pairs with possibly nested arrays in these values.

public class Something
{
    [BsonId, JsonIgnore]
    public ObjectId _id { get; set; }

    public IDictionary<string, object> data { get; set; }
}

When the json is posted from the client, Json.NET deserializes properly.

Saving the class to MongoDB, I get something like this in the database with the c# specific type:

{
    property1: 'one',
    property2: {
        _t: 'System.Collections.Generic.List`1[System.Object]'
        _v:
        [
            {}, {}, {}, ...
        ]
     }
}

Based on these sources, I have pulled together a CustomCreationConverter for Json.NET that nests a List into the value of the Dictionary:

Apply JsonDictionaryAttributes to properties

Json.NET: Deserializing nested dictionaries

CustomCreationConverter Source Code

public class Something
{
    ...
    [JsonProperty(ItemConverterType = typeof(CustomConverter))]
    public IDictionary<string, object> data { get; set; }
}

with this override:

public class CustomConverter : CustomCreationConverter<IList<object>>
{
    public override IList<object> Create(Type objectType)
    {
        return new List<object>();
    }

    public override bool CanConvert(Type objectType)
    {
        return true; // Just to keep it simple
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartArray)
            return base.ReadJson(reader, objectType, existingValue, serializer);

        return serializer.Deserialize(reader);
    }
}

This works actually fine, but I still get the construction with the c# specific types in MongoDB. How can I get this data into MongoDB without the type-value properties when nested?

Community
  • 1
  • 1
Benjamin E.
  • 5,042
  • 5
  • 38
  • 65
  • A long shot, but does it work with Dictionary<> instead of IDictionary<>. I have all this working with the former. – cirrus Nov 19 '12 at 19:48
  • You are right Cirrus, it actually does work. I modified above question to include my progress and make it more clear. I still don't get the data into MongoDB in a useful way. Anyway, thanks for taking a look. – Benjamin E. Nov 21 '12 at 10:45

1 Answers1

3

So I have this working with Dictionary<> rather than IDictionary<> without an custom converters, but I'm also using my own types rather than just object. I'm not clear what you mean by "useful way", but you can annotate your members with BsonDictionaryOptionsAttribute and pass in DictionaryRepresentation.Document or DictionaryRepresentation.ArrayOfDocument to change the shape persisted to MongoDB if that's what you mean?

Otherwise, what did you mean by "useful way" with regards to the way it's structured now? You'll alwways get the "_t" discriminator as long as there's more than one type possible. I'm guessing that you're seeing that because you're using IDictionary<>.

cirrus
  • 5,624
  • 8
  • 44
  • 62
  • So does that mean the Dictionary representation in mongodb will always be language-dependent, in this case c#? Could I still query the same database with another driver, e.g. for node.js or PHP? When writing data via the console or a GUI the types are neutral and when retrieved from c#, the types are plain objects or object[]-arrays when nested. – Benjamin E. Nov 22 '12 at 02:17
  • I can't imagine why not. the "_t" discriminator is a C# driver thing, it shouldn't affect any other native access method unless they also try to use "_t". The discriminator is only needed here I think because of your IDictionary<> ambiguity. You could try using [BsonDiscriminatorAttribute]. See also these discussions. https://groups.google.com/forum/?fromgroups=#!topic/mongodb-user/hfPwFT8q1Z0 and http://stackoverflow.com/questions/8138330/storing-a-dictionary-with-polymorphic-values-in-mongodb-using-c-sharp – cirrus Nov 22 '12 at 10:02
  • It's a good point though. I wonder they didn't choose to call "_t" "_.net" or something language specific instead but I guess you can infer that from the namespace value easily enough if necessary. – cirrus Nov 22 '12 at 10:03
  • Thanks, as far as my question is formulated I think it's answered. However I'm still struggling with deeper nested Lists and Dictionaries as described here [link](http://stackoverflow.com/questions/10568642/how-to-recursively-deserialize-dynamic-json-into-net-objects-using-json-net-on?lq=1) and here [link](http://stackoverflow.com/questions/5546142/how-do-i-use-json-net-to-deserialize-into-nested-recursive-dictionary-and-list). I can't believe there is no easy solution to that. – Benjamin E. Nov 24 '12 at 07:24
  • 1
    The problem is that a JArray with deeper nested JObjects converts simple elements/key-values into null-arrays and hence do not store the values, no matter which Dictionary representation I choose. I guess my only way out is to work with raw strings. – Benjamin E. Nov 24 '12 at 07:36
  • As far as “deeper nested lists and dictionaries” are concerned, annotations with dictionary options attributes will not be possible, as list or dictionary values cannot be annotated. Instead, a custom version of the MongoDB ObjectSerializer needs to be registered. Please see [my answer to another question](https://stackoverflow.com/questions/19786403/dictionarystring-object-to-bsondocument-conversion-omitting-t-field/48346236#48346236) for an example. – Otto G Feb 06 '18 at 16:35