39

THE STORY

I am using Firebase Realtime Database in my app. I have a model something like this.

class Item {
    int mItemName;
    // Simplified for brevity
}

Now, this stores the field as itemName in my real time database. But I don't want to use that naming convention. I want the naming pattern to be this, item_name.

WHAT I DID

I used the @PropertyName("item_name") above the field like this,

class Item {
        @PropertyName("item_name")
        int mItemName;
        // Simplified for brevity
    }

THE PROBLEM

Firebase seems to just ignore the annotation completely. There is no way I am able to change the property names for serialization and deserialization.

Any help would be highly appreciated.

EDIT

Here is the complete model class in concern,

public class FileModel {

        @PropertyName("file_id")
        String mFileId;
        @PropertyName("file_name")
        String mOriginalFileName;
        @PropertyName("file_path")
        String mFilePath;
        @PropertyName("file_type")
        String mFileType;
        @PropertyName("last_modified")
        Long mFileLastModified;
        @PropertyName("file_size")
        String mFileSize;
        @Exclude
        private boolean mIsSelected;

        /**
         * Must have empty constructor for JSON deserialization by Firebase
         */
        public FileModel() {
        }

        public FileModel(String fileId, String originalFileName,
                                    String filePath, String fileType, Long fileLastModified, String fileSize) {
            this.mFileId = fileId;
            this.mOriginalFileName = originalFileName;
            this.mFilePath = filePath;
            this.mFileType = fileType;
            this.mFileLastModified = fileLastModified;
            this.mFileSize = fileSize;
        }

        public String getFileId() {
            return mFileId;
        }

        public void setFileId(String fileId) {
            this.mFileId = fileId;
        }

        public String getOriginalFileName() {
            return mOriginalFileName;
        }

        public void setOriginalFileName(String originalFileName) {
            this.mOriginalFileName = originalFileName;
        }

        public String getFilePath() {
            return mFilePath;
        }

        public void setFilePath(String filePath) {
            this.mFilePath = filePath;
        }

        public String getFileType() {
            return mFileType;
        }

        public void setFileType(String fileType) {
            this.mFileType = fileType;
        }

        public Long getFileLastModified() {
            return mFileLastModified;
        }

        public void setFileLastModified(Long fileLastModified) {
            this.mFileLastModified = fileLastModified;
        }

        public String getFileSize() {
            return mFileSize;
        }

        public void setFileSize(String fileSize) {
            this.mFileSize = fileSize;
        }

        public boolean getIsSelected() {
            return mIsSelected;
        }

        public void setIsSelected(boolean isSelected) {
            this.mIsSelected = isSelected;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            FileModel model = (FileModel) o;

            if (mIsSelected != model.mIsSelected) return false;
            if (mFileId != null ? !mFileId.equals(model.mFileId) : model.mFileId != null) return false;
            if (mOriginalFileName != null ? !mOriginalFileName.equals(model.mOriginalFileName) : model.mOriginalFileName != null)
                return false;
            if (mFilePath != null ? !mFilePath.equals(model.mFilePath) : model.mFilePath != null)
                return false;
            if (mFileType != null ? !mFileType.equals(model.mFileType) : model.mFileType != null)
                return false;
            if (mFileLastModified != null ? !mFileLastModified.equals(model.mFileLastModified) : model.mFileLastModified != null)
                return false;
            return mFileSize != null ? mFileSize.equals(model.mFileSize) : model.mFileSize == null;

        }

        @Override
        public int hashCode() {
            int result = mFileId != null ? mFileId.hashCode() : 0;
            result = 31 * result + (mOriginalFileName != null ? mOriginalFileName.hashCode() : 0);
            result = 31 * result + (mFilePath != null ? mFilePath.hashCode() : 0);
            result = 31 * result + (mFileType != null ? mFileType.hashCode() : 0);
            result = 31 * result + (mFileLastModified != null ? mFileLastModified.hashCode() : 0);
            result = 31 * result + (mFileSize != null ? mFileSize.hashCode() : 0);
            result = 31 * result + (mIsSelected ? 1 : 0);
            return result;
        }

        @Override
        public String toString() {
            return "FileModel{" +
                    "mFileId='" + mFileId + '\'' +
                    ", mOriginalFileName='" + mOriginalFileName + '\'' +
                    ", mFilePath='" + mFilePath + '\'' +
                    ", mFileType='" + mFileType + '\'' +
                    ", mFileLastModified=" + mFileLastModified +
                    ", mFileSize='" + mFileSize + '\'' +
                    ", mIsSelected=" + mIsSelected +
                    '}';
        }
    }
Aritra Roy
  • 15,355
  • 10
  • 73
  • 107
  • What version of Firebase Database do you include in your build.gradle file? – Frank van Puffelen Jul 31 '16 at 15:37
  • If your class `public`? Is the field `public? Without those two, it will not be serialized to JSON. – Frank van Puffelen Jul 31 '16 at 15:39
  • I am using version 9.2.1, the latest ones. The class and field both are public, and they are serialized as well, just that the annotation to change the property name is not respected. – Aritra Roy Jul 31 '16 at 19:38
  • Check Frank's answer on this thread: http://stackoverflow.com/questions/37330156/new-firebase-json-annotations Apparently, they missed the PropertyName annotation on this release. But they will include it on the next update. – Rosário Pereira Fernandes Jul 31 '16 at 23:07
  • Could you post your entire class definition as an edit to the answer? Then it will be easy for me to debug. – Sam Stern Aug 01 '16 at 02:19
  • @RosárioPereiraFernandes I can see and use the PropertyName annotation in this release itself. How can it not be included then? – Aritra Roy Aug 01 '16 at 04:36
  • @hatboysam It is actually a very large class to post entirely here. But it exactly conforms to the sample one posted above. – Aritra Roy Aug 01 '16 at 04:39
  • Does the class have an empty constructor? – Rosário Pereira Fernandes Aug 01 '16 at 08:22
  • Yes absolutely. The class gets serialized into JSON but the names are not changed as per the annotation. That's the problem. – Aritra Roy Aug 01 '16 at 08:25
  • @AritraRoy could you post the class as a Github Gist link in that case? I won't be able to guess what the problem is without seeing the actual code that is causing it. It could just be a simple typo or another subtle mistake! – Sam Stern Aug 01 '16 at 13:29
  • Can you try adding @PropertyName to the getter and the setter instead of the variable declaration? – Sam Stern Aug 02 '16 at 15:23

5 Answers5

75

Solution for Kotlin data class:

data class Pojo (@get:PropertyName("fieldName") @set:PropertyName("fieldName") var field: String = "")
Hammer
  • 968
  • 1
  • 8
  • 6
  • 6
    Works for me, too. The properties must be vars, and must have both the set and get annotations. – Ari Lacenski Nov 08 '18 at 19:55
  • 2
    @AriLacenski with Cloud Firestore it works fine with vals annotated with a single `@PropertyName`, like this: `data class Pojo(@PropertyName("fieldName") val field: String = "")`. Haven't tested it for the Realtime Database – Max Feb 25 '20 at 18:41
29

Finally got a chance to solve this problem. Thanks to @hatboysam for the suggestion.

The only problem was, @PropertyName annotation was not properly documented in Firebase.

The first thing that is necessary is the the field must be public otherwise the annotation will not work, which is quite obvious/

Now the annotation takes into account both the field name as well as the getter/setter names to serialize. I also had the problem where the fields as well as the getter/setters were getting serialized, resulting in duplicate key/value pairs.

I solved the problem by using the annotation on the field name which were public and ignoring the getter/setters. This solved the problem perfectly. Not the data was properly serialized with the property name I wanted and there was no duplicate data problem as well.

Here is a simple example,

    class Item {

        @PropertyName("item_no")
        int mItemNo;
        // Simplified for brevity

        @Exclude
        public int getItemNo(){
              return mItemNo;
        }

        @Exclude
        public void setItemNo(int itemNo){
              this.mItemNo = itemNo;
        }
    }
pvpkiran
  • 25,582
  • 8
  • 87
  • 134
Aritra Roy
  • 15,355
  • 10
  • 73
  • 107
23

Alternatively just mark your getters with @PropertyName instead of annotating properties themselves - this way you can keep properties private while providing custom names:

public class User extends Object {

    private String mDisplayName;


    @PropertyName("userName")
    public String getDisplayName() {
        return mDisplayName;
    }

    @PropertyName("userName")
    public void setDisplayName(String displayName) {
        mDisplayName = displayName;
    }

}
vir us
  • 9,920
  • 6
  • 57
  • 66
3

The Kotlin solution for data classes with immutable fields :

data class Item(
    @get:PropertyName("item_name")
    val mItemName: Int = 0 // Simplified for brevity
)

The Kotlin solution for data classes with mutable fields :

data class Item(
    @set:PropertyName("item_name")
    @get:PropertyName("item_name")
    var mItemName: Int = 0 // Simplified for brevity
)
Julian Paul
  • 162
  • 1
  • 2
0

If you're using JAVA and the other solutions not working for you then this will definitely work!

I wanted like this where every variable's first letter started with Capital.

enter image description here

public class CommentModel {

@PropertyName("BuyerID")
Integer buyerId;
@PropertyName("Message")
String message;
@PropertyName("Name")
String name;
@PropertyName("Photo")
String photo;
@PropertyName("SellerID")
Integer sellerId;

public CommentModel() {

}

@PropertyName("BuyerID")
public Integer getBuyerId() {
    return buyerId;
}

@PropertyName("BuyerID")
public void setBuyerId(Integer buyerId) {
    this.buyerId = buyerId;
}

@PropertyName("Message")
public String getMessage() {
    return message;
}

@PropertyName("Message")
public void setMessage(String message) {
    this.message = message;
}

@PropertyName("Name")
public String getName() {
    return name;
}

@PropertyName("Name")
public void setName(String name) {
    this.name = name;
}

@PropertyName("SellerID")
public Integer getSellerId() {
    return sellerId;
}

@PropertyName("SellerID")
public void setSellerId(Integer sellerId) {
    this.sellerId = sellerId;
}

@PropertyName("Photo")
public String getPhoto() {
    return photo;
}

@PropertyName("Photo")
public void setPhoto(String photo) {
    this.photo = photo;
}

}

Note that, here @PropertyName("") is com.google.firebase.database.PropertyName

Kishan Solanki
  • 13,761
  • 4
  • 85
  • 82