数据库异常:无法将java.lang.String类型的对象转换为类型

9
我已经查看了几个类似问题的答案,但不明白为什么我的应用程序不能正常运行。
我正在试图让我的应用程序从Firebase中读取命令并移动一个无人机。Firebase中的命令来自另一种软件。该应用程序是基于Parrot Drone SDK示例代码构建的。
它似乎能够从命令对象中获取文本并将其附加到一个文本视图上,但当添加新的子节点时,它就会崩溃。当添加新的子节点时,我会收到以下错误信息。
E/AndroidRuntime: FATAL EXCEPTION: main
                Process: com.parrot.sdksample, PID: 10592
                com.google.firebase.database.DatabaseException: Can't convert object of type java.lang.String to type com.parrot.sdksample.activity.CommandObject
                    at com.google.android.gms.internal.zg.zzb(Unknown Source)
                    at com.google.android.gms.internal.zg.zza(Unknown Source)
                    at com.google.firebase.database.DataSnapshot.getValue(Unknown Source)
                    at com.parrot.sdksample.activity.MiniDroneActivity$13.onChildAdded(MiniDroneActivity.java:383)
                    at com.google.android.gms.internal.px.zza(Unknown Source)
                    at com.google.android.gms.internal.vj.zzHX(Unknown Source)
                    at com.google.android.gms.internal.vp.run(Unknown Source)
                    at android.os.Handler.handleCallback(Handler.java:739)
                    at android.os.Handler.dispatchMessage(Handler.java:95)
                    at android.os.Looper.loop(Looper.java:148)
                    at android.app.ActivityThread.main(ActivityThread.java:5417)
                    at java.lang.reflect.Method.invoke(Native Method)
                    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

我的Firebase数据结构示例如下。
{
"drones" : {
    "commands" : {
    "-L-n9HwaQktOdI2VEVlH" : {
        "executed" : false,
        "text" : "TAKE_OFF",
        "timestamp" : 1.512686825309134E9
    },
    "-L-nAuK5Ifde7Cdnan8K" : {
        "executed" : false,
        "text" : "LAND",
        "timestamp" : 1.512687248764272E9
    }
    }
}
}

我在我的活动中从Firebase获取数据的函数如下所示。
private void initFirebase(){
        mCommandTextView = (TextView) findViewById(R.id.commandTextView);

        FirebaseDatabase database = FirebaseDatabase.getInstance();
        DatabaseReference commandsRef = database.getReference("drones/commands");

        ChildEventListener childEventListener = new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
                Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
                CommandObject command = dataSnapshot.getValue(CommandObject.class);
                mCommandTextView.append(command.text + "\n");
// I've tried commenting out the if statements below this, but it doesn't seem to make a difference.
                if ("TAKE_OFF".equals(command.text)) {
                    mMiniDrone.takeOff();
                } else if ("LAND".equals(command.text)) {
                    mMiniDrone.land();
                }
            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName){

            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {

            }

            public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName){

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        };
        commandsRef.addChildEventListener(childEventListener);
    }

我的CommandObject类看起来像这样。

public class CommandObject {
    public String text;
    public float timestamp;
    public boolean executed;

    public CommandObject() {
    }

    public CommandObject(boolean executed, String text, float timestamp){
        this.executed = executed;
        this.text = text;
        this.timestamp = timestamp;
    }
}

我也尝试过使用值事件监听器,但是出现了同样的问题。

1
乍一看,您分享的代码和JSON相匹配。您确定/drones/commands下没有其他格式不同的子元素吗? - Frank van Puffelen
是的,这个 JSON 是从 Firebase 的根目录导出的。 - Desmond Chin
7个回答

9
您遇到了这个错误:
Can't convert object of type java.lang.String to type com.parrot.sdksample.activity.CommandObject

因为您正在尝试读取类型为CommandObjectString数据,所以会出现此错误。
更简单的方法是使用String类来获取这些值,如下所示:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference commandsRef = rootRef.child("drones").child("commands");
ValueEventListener eventListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for(DataSnapshot ds : dataSnapshot.getChildren()) {
            boolean executed = ds.child("executed").getValue(Boolean.class);
            String text = ds.child("text").getValue(String.class);
            double timestamp = ds.child("timestamp").getValue(Double.class);
            Log.d("TAG", executed + " / " + text + " / " + timestamp);
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {}
};
commandsRef.addListenerForSingleValueEvent(eventListener);

以下是使用CommandObject类对象的方法:

DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference commandsRef = rootRef.child("drones").child("commands");
ValueEventListener eventListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for(DataSnapshot ds : dataSnapshot.getChildren()) {
            CommandObject commandObject = ds.getValue(CommandObject.class);
            Log.d("TAG", commandObject.getExecuted() + " / " + 
                commandObject.getText() + " / " + 
                commandObject.getTimestamp());
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {}
};
commandsRef.addListenerForSingleValueEvent(eventListener);

在这两种情况下,你的输出结果将相同:
false / TAKE_OFF / 1.512686825309134E9
false / LAND / 1.512687248764272E9

4

Alex提供的解决方案有效,但没有完全回答为什么我不能将数据存储在一个对象中,对于我的应用程序,当子组件添加监听器时,应用程序可以读取已经在Firebase上的数据,但是当新的子组件被添加时,数值为空。

    onChildAdded:DataSnapshot { key = -L0JjGo-3QMYDsuTMQcN, value =  }

我进行了进一步的调查,发现这可能是因为不是所有的值都在同一时刻写入所致。通过查看Firebase,似乎先添加键,然后再添加值。这就是为什么错误显示无法转换,因为它不存在。
我正在使用Python Firebase Admin SDK将数据添加到Firebase,所以我不确定是否是原因。
为了解决我的问题,我将代码移动到onChildChanged函数中,并添加了一个检查,只有在我需要的所有数据都存在时才运行代码。这样,我可以立即将值存储在对象中。
    @Override
    public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName){
        if (dataSnapshot.child("text").exists() &&
                dataSnapshot.child("executed").exists() &&
                dataSnapshot.child("timestamp").exists()){
            Log.d(TAG, "onChildChanged:" + dataSnapshot.toString());
            CommandObject command = dataSnapshot.getValue(CommandObject.class);
            if ("TAKE_OFF".equals(command.text)) {
                mMiniDrone.takeOff();
            } else if ("LAND".equals(command.text)) {
                mMiniDrone.land();
            }
        }
    }

我在你的问题中没有看到你想从对象中获取数据。我相应地更新了我的答案。你在你的答案中所说的“是由于值不是同时写入的”,是 **不正确的**。你得到这个错误是因为你试图读取类型为String的数据,而它是CommandObject类型的,这就是你得到这个错误的原因。这是唯一的原因,也是我提供给你那个答案并使用String类的原因。 - Alex Mamo

2

Alex为我提供了正确的解决方案,但由于我使用firebase-ui和kotlin,所以使用的代码是不同的。更多信息请参见此处

    val options = FirebaseRecyclerOptions.Builder<ChatMessage>()
            .setQuery(query, ChatMessage::class.java)
            .build()

    // equivalent options object with manual parsing, use it to debug which field gives error
    val options = FirebaseRecyclerOptions.Builder<ChatMessage>()
            .setQuery(query, object : SnapshotParser<ChatMessage> {

                override fun parseSnapshot(snapshot: DataSnapshot): ChatMessage {

                    val senderId = snapshot.child("senderId").getValue(String::class.java)
                    val receiverId = snapshot.child("receiverId").getValue(String::class.java)
                    val senderName = snapshot.child("senderName").getValue(String::class.java)
                    val text = snapshot.child("text").getValue(String::class.java)
                    val timestamp = snapshot.child("timestamp").getValue(Long::class.java)

                    return ChatMessage(senderId, receiverId, senderName, text, timestamp)
                }

            })
            .build()


    adapterMessages = object : FirebaseRecyclerAdapter<ChatMessage, ChatHolder>(options) {

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatHolder {

            val rootView = LayoutInflater.from(parent.context).inflate(R.layout.chat_message_item, parent, false)

            return ChatHolder(rootView)
        }

        override fun onBindViewHolder(holder: ChatHolder, position: Int, model: ChatMessage) {

            holder.populateItem(model)
        }
    }

1
如果您在数据库中使用地图对象,应直接使用get方法来获取您的对象。 doc: DocumentSnapshot val data = doc.get("location", SaleLocations::class.java)

0

我曾经遇到过类似的问题,无法从数据库中检索数据;出现了一个带有DatabaseException错误信息的错误。

无法将此类型转换为该类型。

我的错误在于我错误地向数据库写入了数据。我写了类似于...

mdatabaseReference.setValue(myObject)

而不是

mdatabaseReference.child(myObject.getId()).setValue(myObject)

所以,我的建议是:如果您在读取数据库时遇到此类错误,那么应确保数据正确地写入了JSON树中的正确节点。

@Desmond Chin 应该分享他写入数据库的代码,这样我们才能确认他在写入数据库时没有发生此类错误。 - Njuacha Hubert

0
在我的情况下,问题是我使用了 Kotlin 类,但是没有默认构造函数。
 constructor() : this(0, "")

0

您可以像这样正常使用构造函数面向对象编程

这样做没有任何错误了

CommandObject command = new CommandObject(ds.child("").getValue()....

请填写并确保您在Firebase数据中的引用不为空


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接