Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# saving any JSON string as a Firestore document

I'm trying to retrieve a document from Firestore, edit it as a plain-text JSON, and then reupload the changes. I'm using JSON.net to deserialize the string to Dictionary<string, object>, and then pass it on to SetAsync();:

public static async void SaveDocumentDataAsync(string documentAccessPath, string documentData)
{
     DocumentReference documentRef = firestoreDb.Document(documentAccessPath);
     Dictionary<string, object> document = JsonConvert.DeserializeObject<Dictionary<string, object>>(documentData);
     await documentRef.SetAsync(document);
}

This works perfectly if the JSON object is simple, like:

{
    "foo": "bar"
}

But if it has nested elements, everything above the root properties gets saved as an empty array (the keys are all correct, though):

{
    "foo": "bar"
    "baz": {
         "property1": 6,          // will be saved to Firestore as "property1": [],
         "property2": ["text", 5] // will be saved to Firestore as "property2": [],
    }

}

The closest I got to some sort of solution is using Dictionary<string, Dictionary<string, object>> but this only works for root and second level properties, everything else is saved as an empty array again.

Is there a simple way, or any way, to just translate JObject to Dictionaries with only the key and the actual values in them?

I've been trying to find something for a good while but none of the other solutions worked, and I can't make custom classes for the documents, because their contents can be anything, no specific structure.

like image 573
Xerren Avatar asked Apr 20 '26 01:04

Xerren


1 Answers

It turns out that JsonConvert.DeserializeObject only parses the first level of data. We need to handle the Dictionary conversion recursively for Firestore to understand our nested document structure properly.

using Newtonsoft.Json.Linq;

public static async Task SaveDocumentDataAsync(string documentAccessPath, string documentData)
{
    DocumentReference documentRef = firestoreDb.Document(documentAccessPath);
    Dictionary<string, object> document = JObjectToDictionary(JObject.Parse(documentData));
    await documentRef.SetAsync(document);
}

private static object JTokenToObject(JToken token)
{
    switch (token.Type)
    {
        case JTokenType.Object:
            return JObjectToDictionary((JObject)token);
        case JTokenType.Array:
            var array = new List<object>();
            foreach (var item in (JArray)token)
            {
                array.Add(JTokenToObject(item));
            }
            return array;
        default:
            return ((JValue)token).Value;
    }
}

private static Dictionary<string, object> JObjectToDictionary(JObject obj)
{
    var result = new Dictionary<string, object>();

    foreach (var property in obj.Properties())
    {
        result[property.Name] = JTokenToObject(property.Value);
    }

    return result;
}
like image 174
se210 Avatar answered Apr 21 '26 14:04

se210