'How can I print out the content of a Dictionary<string, object>?

Firebase Databse Fails to download data when needed in between other similar operations in app.

System.Collections.Generic.Dictionary`2[System.String,System.Object]

Firebase Cannot take do new GetValueASync sometimes in middle of similar operations of app and returns System.Collections.Generic.Dictionary`2[System.String,System.Object] as a snapshot value.

Steps to reproduce:

Setup any unity project with realtime databse perform non stop multiple times : .GetValueAsync().ContinueWithOnMainThread(task => then you will get value as System.Collections.Generic.Dictionary`2[System.String,System.Object] instead of any child/key/database value.

public void aaaa() {
    Reference.Child("Users").OrderByChild("About/XP").StartAt(1).LimitToFirst(12).GetValueAsync().ContinueWithOnMainThread(task => {
      if (task.IsFaulted) {

        return;
      } else if (task.IsCompleted) {
        DataSnapshot Snapshot = task.Result;
        if (Snapshot != null)
          Debug.Log(Snapshot.Value);
        return;
      }
      return;

    });


Solution 1:[1]

Please use loop

foreach( KeyValuePair<string, object> kvp in Snapshot.Value)
{
    Console.WriteLine("Key = {0}, Value = {1}",
        kvp.Key, kvp.Value);
}

Solution 2:[2]

The "issue"

Well this happens if you use ToString (which is what Debug.Log does internally) on a dictionary or in general any object of a type that doesn't implement it explicitly, it simply returns the same as GetType().FullName.

Default implementations of the Object.ToString method return the fully qualified name of the object's type.

Firebase.Database.DataSnapshot.Value

returns the data contained in this snapshot as native types. The possible types returned are:

  • bool
  • string
  • long
  • double
  • IDictionary{string, object}
  • List{object}

This list is recursive; the possible types for object in the above list is given by the same list.

These types correspond to the types available in JSON.

So it looks like in your case the type is a Dictionary<string, object>.

You can easily confirm this

Debug.Log(new Dictionary<string, object>().ToString());

will print

System.Collections.Generic.Dictionary`2[System.String,System.Object]

Solution 1a - Simple Loop

If you want to see all first level items you rather want to do e.g.

foreach(var kvp in Snapshot.Value)
{
    Debug.Log($"Key: {kvp.Key}, Value: {kvp.Value}");
}

Note though: while the key is a string, the value once again might be a type (afaik e.g. again a Dictionary<string, object>) not implementing ToString in which case again it will simply print out the type name.

Solution 1b - Recursive print

You could of course implement something covering this all recursive, somewhat like e.g. (untested)

public static class SnapthotExtensions
{
    public static string GetString(this DataSnapshot snapshot)
    {
        return GetString(snapshot.Value);
    }

    private static string GetString(this object obj)
    {
        var sb = new StringBuilder();
        GetString(obj, sb);

        return sb.ToString();
    }

    public static void GetString(object obj, StringBuilder sb, int indent = 1)
    {
        switch (obj)
        {
            case bool b:
                sb.Append(b);
                break;

            case string s:
                sb.Append('"').Append(s).Append('"');
                break;

            case long l:
                sb.Append(l.ToString());
                break;

            case double d:
                sb.Append(d.ToString("G17"));
                break;

            case IDictionary<string, object> dict:
            {
                sb.Append("[\n");
                var i = 0;
                foreach (var kvp in dict)
                {
                    sb.Append(new string(Enumerable.Repeat(' ', indent * 2).ToArray()));
                    sb.Append('"');
                    sb.Append(kvp.Key);
                    sb.Append("\": ");
                    GetString(kvp.Value, sb, indent + 1);

                    if (i < dict.Count - 1) sb.Append(",\n");

                    i++;
                }

                sb.Append('\n');
                sb.Append(new string(Enumerable.Repeat(' ', indent * 2).ToArray()));
                sb.Append(']');
                break;
            }

            case IList iList:
                var list = iList.Cast<object>().ToList();

                sb.Append("[\n");
                for (var i = 0; i < list.Count; i++)
                {
                    sb.Append(new string(Enumerable.Repeat(' ', indent * 2).ToArray()));
                    GetString(list[i], sb, indent + 1);
                    if (i < list.Count - 1) sb.Append(",\n");
                }

                sb.Append('\n');
                sb.Append(new string(Enumerable.Repeat(' ', indent * 2).ToArray()));
                sb.Append(']');
                break;

            default:
                throw new NotSupportedException($"Type {obj.GetType()} is not supported!");
        }
    }
}

and then use

Debug.Log(snapshot.GetString());

Solution 2 - Newtonsoft JSON

But if you really want to print out the entire structure (as far as the values are serializable) you could/should rather use Newtonsoft JSON.Net and convert the entire dictionary into a human readable JSON format. Then print out this JSON instead of implementing it yourself.

See Serialize a Dictionary

string json = JsonConvert.SerializeObject(Snapshot.Value, Formatting.Indented);
Debug.Log(json);

Solution 3 - Firebase specific

I found out that Newtonsoft isn't really needed in your specific case! (I'll leave it here as a general hack to print out dictionaries when not using Firebase)

Just use GetRawJsonValue

returns the data contained in this snapshot as a json serialized string.

so it is as simple as

Debug.Log(Snapshot.GetRawJsonValue());

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ganesh Gadekar
Solution 2