Android 如何在 firebase 9.2 中使用自动值

How to use auto-value with firebase 9.2 in Android

我想使用 auto-value with firebase 9.2.0+。我有以下代码:

@AutoValue
public abstract class Office {

    public static Builder builder() {
        return new AutoValue_Office.Builder();
    }

    public abstract double latitud();
    public abstract double longitud();

    @AutoValue.Builder
    public static abstract class Builder {

        public abstract Builder latitud(double latitud);
        public abstract Builder longitud(double longitud);

        public abstract Office build();

    }

}

但是当我调用它时 Office office = childDataSnapshot.getValue(Office.class); 我收到了这个错误:

com.google.firebase.database.DatabaseException: No properties to serialize found on class com.example.app.model.Office

有人知道我为什么会收到此错误以及如何解决吗?我读到 firebase 不再使用 jackson 进行 json 序列化。所以我不确定如何指定一种 @JsonProperty("latitud") 我已经使用 @PropertyName 失败了。

我还尝试重命名抽象方法,如 public abstract double getLatitud();,之后错误是下一个:

java.lang.InstantiationException: Can't instantiate abstract class com.example.app.model.Office

所以我不确定如何解决这个问题。

解决方案

感谢 hatboysamFrank van Puffelen 我终于可以用下一个解决方案来解决这个问题了。

  1. 我根据 hatboysam 答案和 Frank van Puffelen[=53= 创建了一个 FirebaseUtil enum,其中包含两种序列化和反序列化 Firebase 对象的方法] 评论。
  2. 我创建了几个 UserPhone 类 用于测试。
  3. 依赖关系:

    compile 'com.fasterxml.jackson.core:jackson-annotations:2.8.0'
    compile 'com.fasterxml.jackson.core:jackson-databind:2.8.0'
    
  4. 用法示例:

    User user = FirebaseUtil.deserialize(dataSnapshot, User.class);
    Map<String, Object> map = FirebaseUtil.serialize(user);
    

我不确定默认的 Firebase 数据映射器是否可行,但有一个可能的解决方法。首先让我们解释一下您看到的错误:

com.google.firebase.database.DatabaseException: No properties to serialize found on class com.example.app.model.Office

Firebase 映射器查找 public 字段或以 getFoo/setFoo 模式命名的字段。因此,在您的 class 上,映射器看不到任何属性。

java.lang.InstantiationException: Can't instantiate abstract class com.example.app.model.Office

我认为这就是您出行时遇到的麻烦。为了使反序列化工作,您的 class 需要有一个 public,映射器可以通过反射调用的无参数构造函数 (newInstance())。据我所知,这不是 AutoValue 的工作方式。

但不要失去希望!。根据 this github issue,有一种方法可以使用 @JsonCreator 注释使 Jackson 和 AutoValue 兼容。因此,您需要同时使用 Jackson 和 Firebase 来完成这里的工作。

序列化:

// Convert to a Map<String,Object> using Jackson and then pass that to Firebase
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.convertValue(office, Map.class);
databaseReference.setValue(map);

反序列化:

// Use Firebase to convert to a Map<String,Object>
GenericTypeIndicator<Map<String,Object>> t = new GenericTypeIndicator<Map<String,Object>>() {};
Map<String,Object> map = dataSnap.getValue(t);

// Use Jackson to convert from a Map to an Office object
ObjectMapper mapper = new ObjectMapper();
Office pojo = mapper.convertValue(map, Office.class);

我为此写了一个 AutoValue 扩展:

https://github.com/mattlogan/auto-value-firebase

该扩展生成一个与 firebase 兼容的 class,称为 FirebaseValue,作为您生成的 AutoValue class 中的静态内部 class。您可以通过生成的构造函数在 AutoValue class 和 FirebaseValue class 之间进行转换。

这是从自述文件中复制的示例:

@AutoValue @FirebaseValue
public abstract class Taco {

  public static Taco create(String name, List<Ingredient> ingredients, Review review) {
    return new AutoValue_Taco(name, ingredients, review);
  }

  // Create AutoValue_Taco instance from AutoValue_Taco.FirebaseValue instance
  public static Taco create(DataSnapshot dataSnapshot) {
    AutoValue_Taco.FirebaseValue taco = dataSnapshot.getValue(AutoValue_Taco.FirebaseValue.class);
    return new AutoValue_Taco(taco);
  }

  // Create AutoValue_Taco.FirebaseValue instance from AutoValue_Taco instance
  public Object toFirebaseValue() {
    return new AutoValue_Taco.FirebaseValue(this);
  }

  public abstract String name();

  public abstract List<Ingredient> ingredients();

  public abstract Review review();
}