【问题标题】:Firestore: (21.4.1) [CustomClassMapper]: No setter/fieldFirestore:(21.4.1)[CustomClassMapper]:没有设置器/字段
【发布时间】:2020-03-23 22:12:41
【问题描述】:

我有一个User 类,它可以为用户保存一些额外的数据。此数据存储在/来自 Firestore。我有几个正在工作的字段(namesurnamelastLogin),但其中一些字段不工作(blocked)。

当我公开该字段时,它们可以工作,但是当我尝试使用 setter 时,它不会。我尝试清理构建并重建它。我知道由于@Exclude,它没有保存该字段,这是有意的。

我做错了什么?字段类型无关紧要,我添加了一个新的 String 字段,它给出了相同的警告,而 namesurname 工作。

数据库:

**userid**
  {
    "name" : "John",
    "surname" : "Doe",
    "lastLogin" : **timestamp**,
    "blocked" : true
  }

班级:

@Keep
public class User
{
    private String name;
    private String surname;
    private Date lastLogin;
    private boolean blocked = false;

    public User()
    {
    }

public String getName()
{
    return name;
}

public void setName(String name)
{
    this.name = name;
}

public String getSurname()
{
    return surname;
}

public void setSurname(String surname)
{
    this.surname = surname;
}

public Date getLastLogin()
{
    return lastLogin;
}

public void setLastLogin(Date lastLogin)
{
    this.lastLogin = lastLogin;
}

@Exclude
public boolean isBlocked()
{
    return blocked;
}

public void setBlocked(boolean blocked)
{
    this.blocked = blocked;
}

【问题讨论】:

    标签: java android firebase google-cloud-firestore pojo


    【解决方案1】:

    您的代码中的问题是User 类中的构造函数是private。这不是创建类的新实例的正确方法。 JavaBeans require a no-argument constructor 出席。

    当 Cloud Firestore SDK 反序列化来自数据库的对象时,它要求所有正在使用的对象都必须具有此 public 无参数构造函数,以便它可以使用它来实例化对象。对象中的字段是通过使用公共 setter 方法或直接访问公共成员来设置的,正如您已经尝试过的那样。

    因为您的构造函数是私有的,所以 SDK 并不真正知道如何创建它的实例。因此,必须将其更改为公开。创建该类的正确方法应该是:

    class User {
        private String name;
        private String surname;
        private long lastLogin;
        private boolean blocked = false;
    
        public User() {} //Needed for Cloud Firestore
    
        public User(String name, String surname, long lastLogin, boolean blocked) {
            this.name = name;
            this.surname = surname;
            this.lastLogin = lastLogin;
            this.blocked = blocked;
        }
    
        //Getters and setters are not mandatory
    }
    

    另外请注意,setter 和 getter 不是必需的。 Setter 始终是可选的,因为如果 JSON 属性没有 setter,Firebase 客户端会将值直接设置到字段中。


    编辑:

    根据您的评论:

    但它没有解释为什么某些字段有效而其他字段无效。它根本不应该工作,对吧?

    是的,没错,一切都应该工作。其中一些不起作用的原因是您的User 类中的blocked 属性是boolean 类型,而在您的数据库中是String 类型,这是不正确的。两种类型必须匹配。

    而私有构造函数是由于单例实例,据我所知,构造函数应该是私有的,以避免创建类的新实例。

    不,构造函数必须是公开的。我认为有一个误解。每次使用 FirebaseDatabase.getInstance() 时,都会打开应用程序和 Firebase 服务器之间的单个套接字连接。从那一刻起,应用程序和数据库之间的所有流量都通过同一个套接字。所以不管你创建一个实例多少次,它总是一个连接。关于您的 POJO 类,不需要这样的 Singleton,因为 Firebase 始终需要知道如何使用公共无参数构造函数创建该类的实例。

    【讨论】:

    • 我不太确定,但我相信我已经尝试过公共构造函数。我会再试一次,但它没有解释为什么有些领域正在工作而另一些领域却没有。它根本不应该工作,对吧?而私有构造函数是由于单例实例,据我所知构造函数应该是私有的,以避免创建类的新实例。
    • 我现在明白了。请检查我更新的答案。我希望现在更清楚了。是吗?
    • 感谢编辑,但仍不清楚。 lastLogin 确实有效,至少它将它保存在数据库中(我正在使用 Firestore,如前所述),并且在阅读时我没有收到警告。我的课使用了单例,因为我需要它是单例的(这里简化了类)。我知道 Firebase 也是单例的,但这没关系。
    • 哦,对不起。是的,lastLogin 应该像 Date 类型一样工作。如果是 User 类,则不需要 Singleton。只要 Firebase 无法使用公共无参数构造函数创建新实例,您就会始终拥有该行为。所以这确实很重要。
    • 没问题,它被隐藏了一点。我删除了我的单例变体,公开了无参数构造函数并再次尝试。仍然收到相同的消息。您还有其他可能的解决方案吗?可能是带有缓存和 ProGuard 的东西(尽管我使用 @keep 来确保它是可序列化的)?
    【解决方案2】:

    尝试为所有类属性以及非参数构造函数创建一个带有参数的构造函数,然后在您存储在 firebase 中的 java 类中,从用户创建对象并传递它。

    例如:

     package com.example.spacing.Model;
    
     public class User {
    
    private String username;
    private String phone;
    private String id;
    private String imageURL;
    
    private String email;
    
    public User(String username, String email ,String phone, String id, String imageURL) {
        this.username = username;
        this.email=email;
        this.phone = phone;
        this.id = id;
        this.imageURL = imageURL;
    }
    
    public String getImageURL() {
        return imageURL;
    }
    
    public String getEmail() {
        return email;
    }
    
    public void setEmail(String email) {
        this.email = email;
    }
    
    public String getId() {
        return id;
    }
    
    public void setId(String id) {
        this.id = id;
    }
    
    public User() {
    }
    
    public String getUsername() {
        return username;
    }
    
    public void setUsername(String username) {
        this.username = username;
    }
    
    public String getPhone() {
        return phone;
    }
    
    public void setPhone(String phone) {
        this.phone = phone;
    }
       }
    

    FirebaseDatabase.getInstance().getReference("Users")
    
    .child(FirebaseAuth.getInstance().getCurrentUser().getUid())
                                    .setValue(user);
    

    【讨论】:

      猜你喜欢
      • 2020-01-21
      • 2020-07-15
      • 2018-01-24
      • 2018-07-22
      • 2020-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-09
      • 2020-11-30
      相关资源
      最近更新 更多