Firebase数据 - 错误转换值

时间:2018-05-04 16:48:27

标签: c# json firebase firebase-realtime-database xamarin.android

我两天前发布了关于firebase的问题: Android Firebase - add authenticated user into database

我得到了我需要的帮助,解决了第一个问题。但现在我遇到了一个新问题。我在谷歌上搜索了一段时间,有一些关于这个问题的帖子,但没有解决我的问题。我不想发送上一个问题,所以我发布了一个新问题。

当我尝试从firebase数据库中读取插入的数据时,我收到此错误:

  

Newtonsoft.Json.JsonSerializationException:转换值时出错   “test@user.com”键入“carServiceApp.My_Classes.Account”。路径   '电子邮件',第1行,第24位。

以下是代码:

 private async Task LoadData()
    {
        FirebaseUser users = FirebaseAuth.GetInstance(loginActivity.app).CurrentUser;
        id = users.Uid;

        var firebase = new FirebaseClient(loginActivity.FirebaseURL);
        var items = await firebase.Child("users").Child(id).OnceAsync<Account>();


        foreach (var item in items)
        {
            Account user = new Account();
            user.uid = item.Object.uid;
            user.name = item.Object.name;
            user.lastName = item.Object.lastName;
            user.phone = item.Object.phone;
            user.email = item.Object.email;

            userInput_ime.Text = user.name;
            userInput_prezime.Text = user.lastName;
            userInput_broj.Text = user.phone;
            userInput_email.Text = user.email;
        }

    }

这是firebase数据:

-users
    -jwAP2dYNzJeiF3QlmEIEQoruUkO2
       email: "test@user.com"
       lastName: "user"
       name: "test"
       phone: "12421"
       uid: "jwAP2dYNzJeiF3QlmEIEQoruUkO2"

有趣的是,当我尝试用这个读取数据时:

var items = await firebase.Child("users").OnceAsync<Account>();

这很好用(我上次插入的用户)。但是当我添加'uid'节点时,我就会收到错误。我试图解决这个问题很长一段时间,但我无法理解。我猜帐户类没有问题,因为它适用于没有uid节点的情况,但在添加了另一个child()方法时不起作用。

您可以在顶部的链接中看到其他信息(帐户类代码以及将数据存储到数据库中的方式)。

注意:我尝试在Account类中添加构造函数,但这没有帮助。

2 个答案:

答案 0 :(得分:2)

好的,所以我没有找到解决这个问题的解决方案,也不知道为什么会发生这种情况,但我找到了一个解决方法。我认为它不是理想的解决方案,并且它不能解决现有问题。或许这可能是我不了解firebase逻辑的问题,但这是我想出来的。

所以,考虑到如果我没有指定uid节点就可以正常工作了很明显,firebase中的类和数据存在一些问题,我猜是匹配问题。无论如何,我决定拥有最后一个uid节点,这样我就可以选择特定的用户,并且在firebase中拥有与它全部工作时相同的数据。所以,这就是我将数据插入firebase的方式:

  var item = firebase.Child("users").Child(id).PostAsync<Account>(user);

这创建了用户节点和子节点。 PostAsync方法用随机密钥创建了另一个节点。 所以,当我尝试阅读时:

var data = await firebase.Child("users").Child(id).OnceAsync<Account>();

它没有问题。现在firebase数据如下所示:

   users
     JPKdQbwcXbhBatZ2ihBNLRauhV83
        -LCXyLpvdfQ448KOPKUp 
              email:  "spider@man.com"
              lastName:  "man"
              name:  "spider"
              phone:  "14412"
              uid: "JPKdQbwcXbhBatZ2ihBNLRauhV83"

有一些冗余,我基本上有两个ID,但我不明白如何创建我的课程,所以我可以通过其他方式获取数据,所以我这样做了。它工作正常。

如果有人有更好的解决方案,我很乐意改变它。干杯

答案 1 :(得分:1)

这应该是评论,但这只是对需要此问题帮助的任何人的补充。

我知道这个答案已经存在了一段时间,但这似乎仍然与Firebase及其规则的使用有关。我遇到了一个看起来像这样的复杂结构

-Orders
    -9876trfghji (User ID)
     -0
       BusnID: "ty890oihg"
       Name: "Some Name"
       AddOns: Object
       ItemData: Object(containing other objects)
       UserID: "9876trfghji"

注意:在这种情况下以及在Cordas情况下,您将看到两个最终对象都有一个UserID或uid。

我还遇到了对象的反序列化问题,当对象数据发送回设备时,在对象数据中没有实际的用户ID。

您“冗余”使用用户ID的原因是出于使用Firebase规则的安全措施。具有上述结构的第一个UserID能够根据用户ID来控制对信息的访问,而无需在规则中包含额外的验证子句。目前,截至本文发布时,以下规则将根据用户ID保护数据。

“Orders” : {
        "$uid":{
                  ".read":"auth != null",
                  ".write":"auth.uid == $uid"
                }
    }

这仅允许具有授权用户ID的用户写入内容,但具有有效凭据的任何人都可以查看数据。

第二个用户ID必须放置在对象中,因为没有它,您将无法对对象进行标准转换,因为您的对象将没有创建对象所需的所有数据。无论您使用的是GoogleGson还是Newtonsoft.Json之类的软件包,该对象仍未满。

除了将用户ID重新输入到对象中之外,还有其他方法可以解决此问题。对于上面提到的对象,我决定仅在个人代码中重新输入用户ID,以节省时间和手动创建的麻烦。

使用Firebase.Database NuGet包,您可以手动创建对象。这是Cordas问题中的物体的一个例子

    public static void GetUser_Firebase(User user, FirebaseApp app)
    {
    FirebaseDatabase database = FirebaseDatabase.GetInstance(app);
    DatabaseReference reference = database.GetReference($"/users/{user.UserID}");
    //"Using for getting firebase information", $"/users/{user.UserID}"
    reference.AddListenerForSingleValueEvent(new UserInfo_DataValue());
    }

class UserInfo_DataValue : Java.Lang.Object, IValueEventListener
{
    private string ID;
 public UserInfo_DataValue(string uid)
 {
      this.ID = uid;
    }
    public void OnCancelled(DatabaseError error)
    {
        //"Failed To Get User Information For User "
    }

    public void OnDataChange(DataSnapshot snapshot)
    {
        Dictionary<string, string> Map = new Dictionary<string, string>();
        var items = snapshot.Children?.ToEnumerable<DataSnapshot>(); // using Linq
        foreach(DataSnapshot item in items)
        {
            try
            {
                Map.Add(item.Key, item.Value.ToString()); // item.value is a Java.Lang.Object
            }
            catch(Exception ex)
            {
                //"EXCEPTION WITH DICTIONARY MAP"
            }
        }
        User toReturn = new User();
        toReturn.UserID this.ID;
        foreach (var item in Map)
        {
            switch (item.Key)
            {
                case "email":
                    toReturn.email = item.Value;
                    break;
                case "lastName":
                    toReturn.lastName = item.Value;
                    break;
                case "name":
                    toReturn.name = item.Value;
                    break;
                case "phone":
                    toReturn.phone = item.Value;
                    break;                        
            }
        }
    }
}

更新 我想提一提的是,我在写这篇文章时就忽略了这一点,那就是将Firebase.Database NuGet包与Gson NuGet包和Newtonsoft.Json库一起使用

如果您决定使用FIrebase.Database库,只需知道您将非常接近Java.Lang和Java.Util库。像Java.Lang.Object这样的对象编写反序列化数据所需的代码可能非常困难且耗时,但是请不要担心Gson在这里!

如果允许的话,Gson软件包可以让您减轻繁重的工作量,从而可以进行类反序列化。 Gson是一个库,它将允许您对Java.Lang.Obj进行json字符串反序列化。我知道这看起来很不可思议,将它递给一个对象取回字符串听起来很直觉,但我只能忍受。

这里是一个示例,该示例说明了如何将带对象问题的Gson库带给我们。

 public static void Get_User(User user, FirebaseApp app)
    {
        FirebaseDatabase database = FirebaseDatabase.GetInstance(app);
        DatabaseReference reference = database.GetReference($"Users/{user.UserID}");
        reference.AddListenerForSingleValueEvent(new User_DataValue(user, app));
        //$"Trying to make call for user orders Users/{user.UserID}");
    }

class User_DataValue : Java.Lang.Object, IValueEventListener
    {
        private User User;
        private FirebaseApp app;
        public UserOrderID_Init_DataValue(User user, FirebaseApp app)
        {
            this.User = user;
            this.app = app;
        }
        public void OnCancelled(DatabaseError error)
        {
            //$"Failed To Get User Orders {error.Message}");
        }
        public void OnDataChange(DataSnapshot snapshot)
        {
            //"Data received for user orders");
            var gson = new GsonBuilder().SetPrettyPrinting().Create();

            var json = gson.ToJson(snapshot.Value); // Gson extention method obj -> string
            Formatted_Output("Data received for user order json ", json);

            User user = JsonConvert.DeserializeObject<User>(json); //Newtonsoft.Json extention method string -> object

           //now the user is a fully populated object with very little work
        }

对于将来可能遇到此问题的任何人,我希望这会有所帮助