Bikash
Bikash

Reputation: 1522

How to catch java.lang.RuntimeException: Could not deserialize object from Firebase ClassMapper and add default Values

I am using Firebase UI Recycler Adapter to read data from Firestore

FirestoreRecyclerOptions<Transaction> options =
                new FirestoreRecyclerOptions.Builder<Transaction>()
                        .setQuery(mQuery, Transaction.class)
                        .setLifecycleOwner(this)
                        .build();

        mAdapter = new TransactionAdapter(options, this, getActivity()) {

            @Override
            public void onDataChanged() {
                // Show/hide content if the query returns empty.
                if (getItemCount() == 0) {
                    mRestaurantsRecycler.setVisibility(View.GONE);
                    mEmptyView.setVisibility(View.VISIBLE);
                } else {
                    mRestaurantsRecycler.setVisibility(View.VISIBLE);
                    mEmptyView.setVisibility(View.GONE);
                }
            }

            @Override
            public void onError(FirebaseFirestoreException e) {
                Log.e("Error", ", not initializing RecyclerView",e);
            }
        };
        Log.w("Test", "No6 query, not initializing RecyclerView");
        mRestaurantsRecycler.setLayoutManager(new LinearLayoutManager(getActivity()));
        mRestaurantsRecycler.setAdapter(mAdapter);

i am getting below exception for some corrupted fields in Firestore

java.lang.RuntimeException: Could not deserialize object. Failed to convert a value of type java.lang.String to double (found in field 'amount')
    at com.google.firebase.firestore.util.CustomClassMapper.deserializeError(com.google.firebase:firebase-firestore@@17.1.4:524)
    at com.google.firebase.firestore.util.CustomClassMapper.convertDouble(com.google.firebase:firebase-firestore@@17.1.4:427)
    at com.google.firebase.firestore.util.CustomClassMapper.deserializeToPrimitive(com.google.firebase:firebase-firestore@@17.1.4:304)
    at com.google.firebase.firestore.util.CustomClassMapper.deserializeToClass(com.google.firebase:firebase-firestore@@17.1.4:215)
    at com.google.firebase.firestore.util.CustomClassMapper.deserializeToType(com.google.firebase:firebase-firestore@@17.1.4:180)
    at com.google.firebase.firestore.util.CustomClassMapper.access$200(com.google.firebase:firebase-firestore@@17.1.4:53)
    at com.google.firebase.firestore.util.CustomClassMapper$BeanMapper.deserialize(com.google.firebase:firebase-firestore@@17.1.4:700)
    at com.google.firebase.firestore.util.CustomClassMapper$BeanMapper.deserialize(com.google.firebase:firebase-firestore@@17.1.4:674)
    at com.google.firebase.firestore.util.CustomClassMapper.convertBean(com.google.firebase:firebase-firestore@@17.1.4:503)
    at com.google.firebase.firestore.util.CustomClassMapper.deserializeToClass(com.google.firebase:firebase-firestore@@17.1.4:242)
    at com.google.firebase.firestore.util.CustomClassMapper.convertToCustomClass(com.google.firebase:firebase-firestore@@17.1.4:97)
    at com.google.firebase.firestore.DocumentSnapshot.toObject(com.google.firebase:firebase-firestore@@17.1.4:203)
    at com.google.firebase.firestore.QueryDocumentSnapshot.toObject(com.google.firebase:firebase-firestore@@17.1.4:121)
    at com.google.firebase.firestore.DocumentSnapshot.toObject(com.google.firebase:firebase-firestore@@17.1.4:183)
    at com.google.firebase.firestore.QueryDocumentSnapshot.toObject(com.google.firebase:firebase-firestore@@17.1.4:101)
    at com.firebase.ui.firestore.ClassSnapshotParser.parseSnapshot(ClassSnapshotParser.java:23)
    at com.firebase.ui.firestore.ClassSnapshotParser.parseSnapshot(ClassSnapshotParser.java:12)
    at com.firebase.ui.common.BaseCachingSnapshotParser.parseSnapshot(BaseCachingSnapshotParser.java:35)
    at com.firebase.ui.common.BaseObservableSnapshotArray.get(BaseObservableSnapshotArray.java:52)
    at com.firebase.ui.firestore.FirestoreRecyclerAdapter.getItem(FirestoreRecyclerAdapter.java:83)
    at com.firebase.ui.firestore.FirestoreRecyclerAdapter.onBindViewHolder(FirestoreRecyclerAdapter.java:125)
    at android.support.v7.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:6673)
    at android.support.v7.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:6714)
    at android.support.v7.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:5647)
    at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5913)
    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5752)
    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5748)
    at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2232)
    at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1559)
    at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1519)
    at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:614)
    at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3812)
    at android.support.v7.widget.RecyclerView.onMeasure(RecyclerView.java:3225)
    at android.view.View.measure(View.java:22071)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
    at android.support.design.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:739)

I know why this error is thrown and i also know i can correct this if i correct my amount field in Firestore Database or by having a Cloud Function to check data validation etc.

But I don't want an App crash experience. Is there any way to catch and add any default value or Null for corrupted values. Showing an individual Null or NA field or an entire empty screen is always better than a Crash.

EDIT: In order to set a default value and avoid Runtime exception i have tried this with my Model class. I still don't know if there is a way to catch this exception.

public double amount;
public double getAmount() {
    return amount;
}
// If you change it Object, ClassMapper will not throw any exception and 
// You can parse any data type by yourself and also set a default value.
public void setAmount(Object amount) {
    try {
        // You can also add  instanceOf check
        this.amount = Double.parseDouble(amount.toString());
    } catch (Exception e) {
        // Set your default value
        this.amount = 0.00;
    }
}

Upvotes: 2

Views: 567

Answers (1)

Alex Mamo
Alex Mamo

Reputation: 138944

I'm answering this question becase of your request from here. So because I see that you are using the Firebase-UI library, there is no need to use a try-catch block. As also @FrankvanPuffelen mentioned in his comment, the simplest thing that you can do, is to override the onError method in your adapter class like this:

FirestoreRecyclerAdapter adapter = new FirestoreRecyclerAdapter<Chat, ChatHolder>(options) {
    @Override
    public void onDataChanged() {
        //Called each time there is a new query snapshot.
    }

    @Override
    public void onError(FirebaseFirestoreException e) {
        //Handle the error
        Log.d(TAG, e.getMessage());
    }
};

Upvotes: 4

Related Questions