question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Serializing and deserializing embedded classes (Firestore)

See original GitHub issue

I have recently started using Firestore and your C# library and everything is working great, apart from one thing that is quite important to me, and hopefully other people too.

As you know it’s very easy to serialize and deserialize root properties of custom models by annotating them with the [FirestoreProperty] attribute, however, there doesn’t seem to be a “native” way of doing the same for nested objects. Documentation says that we can embed Dictionary instances in Firestore documents, however, that means we lose the type awareness if we’re resorted to using Dictionary<string, object>.

I am current migrating a project from MongoDB to Firestore and I have quite a few models that are embedded 2-3 levels deep. ConvertTo<T>() method unfortunately doesn’t serialize models containing other embedded models so I ended up writing custom extension methods which convert such models to dictionaries and back. That, however, means I don’t get to use the automatic (de)serialization that the library offers.

From what I can see, the library is using ValueDeserializer by default and there is no way to override or modify it. Are there any plans to add support for nested models? My thinking is when you extract the property values you could check if the value is decorated with the [FirestoreData] attribute and automatically extract it just like the root object. Maybe the same could be done when hydrating a model instance with snapshot data.

Here’s my ideal scenario:

[FirestoreData]
public class Parent {
    [FirestoreProperty]
    public string Name { get; set; }

    [FirestoreProperty]
    public Child Child { get; set; }
}

[FirestoreData]
public class Child {
    [FirestoreProperty]
    public int One { get; set; }

    [FirestoreProperty]
    public int Two { get; set; }
}

var child = new Child
{
    One = 1,
    Two = 2
};

var parent = new Parent
{
    Name = "John",
    Child = child
};

DocumentReference document = await collection.AddAsync(parent);

That would result in this document being stored in Firestore:

{
  "Name": "John",
  "Child": {
    "One": 1,
    "Two": 2
  }
}

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:2
  • Comments:5

github_iconTop GitHub Comments

1reaction
codeaidcommented, Aug 23, 2018

You are indeed correct. I’m not even sure how to explain my stupidity as I was convinced I tried doing it that way. Apparently I haven’t…

I updated the few models that I have at the moment and everything seems to work as expected. One thing I noticed though is that it’s not possible to pass an instance of a model to the Update method of the Transaction class (not sure about other methods).

I’m currently implementing a custom IUserStore version of ASP.NET Identity and the update method is expected to handle concurrency by fetching and persisting the user document in a single transaction. Here is a good example of what I mean.

As you can see it’s not possible to do transaction.Update(userRef, user); as none of the overloads accepts an object. It’s obviously not an issue as it can be “fixed” by having a custom dictionary extractor but it would be nice to be able to just pass the model to the transaction methods without having to convert them manually.

I’ve ended up with the following extension method, which doesn’t extract nested models, but as I only need it for the User model it’s not a problem at the moment as the class doesn’t have any nested classes:

public static Dictionary<string, object> ToDictionary<T>(this T model)
{
    return typeof(T).GetProperties()
        .Where(prop => Attribute.IsDefined(prop, typeof(FirestorePropertyAttribute)))
        .ToDictionary(
            prop => prop.Name,
            prop => prop.GetValue(model, null)
        );
}
1reaction
jskeetcommented, Aug 23, 2018

Okay, I’ve tried it, and it worked as I expected to. Here’s my test code, using Json.NET just to show the structure of the object in a simple way. I’ve used your Parent and Child classes without any modifications.

using Google.Cloud.Firestore;
using Newtonsoft.Json;
using System;
using System.Threading.Tasks;

namespace Issue2444
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string projectId = Environment.GetEnvironmentVariable("FIRESTORE_TEST_PROJECT");
            var db = FirestoreDb.Create(projectId);
            var collection = db.Collection("issue2444");

            var parent = new Parent
            {
                Name = "John",
                Child = new Child { One = 1, Two = 2 }
            };
            Console.WriteLine("Before saving:");
            Console.WriteLine(JsonConvert.SerializeObject(parent));


            var docRef = await collection.AddAsync(parent);

            var fetched = await docRef.GetSnapshotAsync();
            var converted = fetched.ConvertTo<Parent>();

            Console.WriteLine("After fetching:");
            Console.WriteLine(JsonConvert.SerializeObject(converted));
        }
    }
}

Output:

Before saving:
{"Name":"John","Child":{"One":1,"Two":2}}
After fetching:
{"Name":"John","Child":{"One":1,"Two":2}}

That’s working as you intended, right? Could you clarify what behavior you want that isn’t covered?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Deserializing nested firebase firestore classes on Android
I'm trying to deserialize a simple firebase firestore document for an android app. The issue is with kotlin nested classes deserialization.
Read more >
Data model
The Firestore data model revolves around documents and collections. ... In order to deserialize a map as an attributed class, the class must ......
Read more >
Map Cloud Firestore data with Swift Codable - Firebase
Swift's Codable API provides a powerful and flexible way to map data from serialized formats to and from your applications data model. In...
Read more >
Loading a json from Firebase
I have a class that saves two variables into an array. i have managed ... inner json is serialized twice, it need to...
Read more >
Serialize complex (nested) objects and List of objects in Flutter ...
Serialize complex (nested) objects and List of objects in Flutter/Dart · 1. the properties of the class (title and url) and a constructor...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found