14

I'm using the new firebase sdk for android and use the real database feature. When i use the getValue(simple.class) everything is fine. But when i want to parse a class which is a subclass, all the attribute of the mother class are null, and i have this type of error:

No setter/field for name found on class uk.edume.edumeapp.TestChild

public class TestChild  extends TestMother {

    private String childAttribute;

    public String getChildAttribute() {
        return childAttribute;
    }
}

public class TestMother {

    protected String motherAttribute;

    protected String getMotherAttribute() {
        return motherAttribute;
    }
}

this function

snapshot.getValue(TestChild.class);

motherAttribute attribute is null, and I get

No setter/field for motherAttribute found on class uk.edume.edumeapp.TestChild

the Json that i parse is:

{
  "childAttribute" : "attribute in child class",
  "motherAttribute" : "attribute in mother class"
}
Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
drevlav
  • 572
  • 2
  • 6
  • 14

4 Answers4

18

Firebaser here

This is a known bug in some versions of the Firebase Database SDK for Android: our serializer/deserializer only considers properties/fields on the declared class.

Serialization of inherited properties from the base class, is missing in the in releases 9.0 to 9.6 (iirc) of the Firebase Database SDK for Android. It was added back in versions since then.

Workaround

In the meantime you can use Jackson (which the Firebase 2.x SDKs used under the hood) to make the inheritance model work.

Update: here's a snippet of how you can read from JSON into your TestChild:

public class TestParent {
    protected String parentAttribute;

    public String getParentAttribute() {
        return parentAttribute;
    }
}
public class TestChild  extends TestParent {
    private String childAttribute;

    public String getChildAttribute() {
        return childAttribute;
    }
}

You'll note that I made getParentAttribute() public, because only public fields/getters are considered. With that change, this JSON:

{
  "childAttribute" : "child",
  "parentAttribute" : "parent"
}

Becomes readable with:

ObjectMapper mapper = new ObjectMapper();
GenericTypeIndicator<Map<String,Object>> indicator = new GenericTypeIndicator<Map<String, Object>>() {};
TestChild value = mapper.convertValue(dataSnapshot.getValue(indicator), TestChild.class);

The GenericTypeIndicator is a bit weird, but luckily it's a magic incantation that can be copy/pasted.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • i try to make it work using jackson : @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY) @JsonSubTypes({ @JsonSubTypes.Type(value=TestChild.class, name = "child") }) although it did not really help me. Do you know how should i do it? – drevlav May 31 '16 at 14:28
  • 1
    Yuck, no annotations should be needed. Just `new ObjectMapper().convertValue(pojoObject, Map.class)` and then pass the resulting map into `setValue()` – Frank van Puffelen May 31 '16 at 15:00
  • my problem is the deserialisation. I receive a DataSnapShot from firebase. From there what do I do? How do i get the Json string from it? because if I use dataSnapShot.getValue().toString() i get a string without any "" and i can not parse it. – drevlav May 31 '16 at 15:41
  • @FrankvanPuffelen how do I tell your serializer what the json property name is? With firebase and jackson I could use @JsonGetter("my-property") notice this is not a valid java identifier, how do I do it with the new serializer? Thanks! – Greg Ennis Jun 01 '16 at 03:56
  • @FrankvanPuffelen i love your black magic, this GenericTypeIndicator does the job, and helps me a lot. do you know when the upcoming version of Firebase will be out with the fix? as well where should I raise a bug if i encounter another one? big thanks!! – drevlav Jun 01 '16 at 08:51
  • Yup. The fix is in and will be out in an upcoming release (I'm not completely sure which version yet). – Frank van Puffelen Jun 01 '16 at 14:15
  • @FrankvanPuffelen Is there at least an ETA? – Chad Bingham Jun 22 '16 at 03:12
  • 1
    The setup you have here (with public accessors) made no difference on my project. When _saving_ I still am missing all parent data – Chad Bingham Jun 22 '16 at 03:21
  • 1
    The majority of the answer documentation a workaround using Jackson; I marked that section more clearly just now. The exact code I have there worked when I tested it. If you're having trouble applying the same workaround to your case, I suggest opening a question with the minimal, complete code that reproduces the problem. – Frank van Puffelen Jun 22 '16 at 03:53
  • 2
    @FrankvanPuffelen I'm guessing theres a way to do this in reverse, I.E. setting a value in firebase using a parent/child class type? (e.g. reference.setValue(testChild);) Thanks for the workaround, however this seems like a big hit for anyone using many complicated objects who rely on extending. Has there been any update or any alternatives? I think manually creating the child classes will be the better choice for me here rather than objectMapping everything for every call, although I'll feel dirty in doing so.. – Alan Jul 14 '16 at 13:56
  • This is a big issue. The workaround lets vanish one of the main improvements in Firebase 9.2. I'm not going to add Jackson to our project. Just to clarify: Making the getter public doesn't help without using Jackson? – cybergen Jul 23 '16 at 22:45
  • @FrankvanPuffelen can you comment on the status of this? Someone linked release notes below that seem to indicate it has been fixed, but in 10.0.1 my inherited members are still not serialized. – Travis Christian Jan 11 '17 at 21:56
  • It should've been fixed from somewhere mid-September from what I can find. If you're still having a problem, please post an [MCVE](http://stackoverflow.com/help/mcve). Since it may be a different problem, I'd post it as a new question. We can always mark as a dupe as it turns out this one wasn't fixed after all. – Frank van Puffelen Jan 12 '17 at 05:33
  • @FrankvanPuffelen my bad- I got this confused with the related issue of member visibility (http://stackoverflow.com/q/40004698/213156) When I declare the inherited members as public in the superclass it works. Thanks. – Travis Christian Jan 12 '17 at 17:27
6

This was apparently finally fixed in release 9.6.

Fixed an issue where passing a derived class to DatabaseReference#setValue() did not correctly save the properties from the superclass.

JaviCasa
  • 698
  • 8
  • 17
2

for:

No setter/field for motherAttribute found on class uk.edume.edumeapp.TestChild

put setter for TestChild class:

 public class  TestMother {

     private String motherAttribute;

     public String getMotherAttribute() {
         return motherAttribute;
     }

     //set
     public void setMotherAttribute(String motherAttribute) {
         this.motherAttribute= motherAttribute;
     }
 }
Farzad
  • 1,975
  • 1
  • 25
  • 47
  • the childAttribute is fine, during the deserialisation it is set up with the right value, it is the motherAttribute which is null. Since it is defined in the mother class, it is not take in account – drevlav May 31 '16 at 13:59
  • the problem is not the setter, it is that it does not see the field defined in the mother class – drevlav May 31 '16 at 14:03
  • I finally noticed what you have different here: you made `getMotherAttribute()` public. That's the key difference. Good work: upvoted. – Frank van Puffelen May 31 '16 at 20:04
  • 1
    moving it from protected to public does not help, the child has already access to all the "protected" functions so this change is irrelevant. Moreover is the answer provided he says to put a setter in TestChild and show the code where a setter is put in the TestMother, not so consistent. Anyway put a setter for a Mother class attribute in a child class would have no sense and would break the polymorphism pattern... – drevlav Jun 01 '16 at 08:13
  • Write a setter/getter in the subclass which just calls the relevant `super()` method. For example: `protected String getMotherAttribute() { return super.getMotherAttribute(); }` . This works for me. And when they fix the bug you can just delete those... – Alaa M. Oct 13 '16 at 17:31
0

Check this https://firebase.google.com/support/guides/firebase-android

it says

"If there is an extra property in your JSON that is not in your Java class, you will see this warning in the log files: W/ClassMapper: No setter/field for ignoreThisProperty found on class com.firebase.migrationguide.ChatMessage "

Blockquote

You can get rid of this warning by putting an @IgnoreExtraProperties annotation on your class. If you want Firebase Database to behave as it did in the 2.x SDK and throw an exception if there are unknown properties, you can put a @ThrowOnExtraProperties annotation on your class.

Blockquote

Akshay Sahai
  • 2,121
  • 1
  • 17
  • 20