使用Boost ASIO+RapidJSON传输对象时IsObject()断言失败求助
这个错误的核心原因很明确:当你尝试用document["key"]访问成员时,document并不是一个有效的JSON对象。结合你的代码和输出来看,问题出在UDP接收的字符串处理不当,导致RapidJSON解析失败。
问题根源
你用boost::array<char, 2048> recv_buf接收数据,然后直接通过std::string json(recv_buf.data())构造字符串。但UDP接收的实际数据长度远小于2048,这就导致构造出的字符串包含了recv_buf中从有效数据结束到数组末尾的所有垃圾字符(比如输出里开头的E�)。这些垃圾字符破坏了JSON的语法,让RapidJSON解析失败,最终document不是一个合法的Object,触发了断言。
解决方案
1. 正确截取实际接收的字节数
receive_from函数会返回实际接收的字节数,你需要用这个长度来构造字符串,只保留有效数据:
void BoostClient::receivePacket() { boost::array<char, 2048> recv_buf; udp::endpoint sender_endpoint; // 保存实际接收的字节数 size_t bytes_received = static_cast<udp::socket *>(socket_)->receive_from( boost::asio::buffer(recv_buf), sender_endpoint); // 仅使用前bytes_received个字符构造JSON字符串 std::string json(recv_buf.data(), bytes_received); std::cout << "received: " << json << std::endl; Data data(json); data.print(); };
2. 增加解析错误检查
即使字符串正确,也应该在解析时做合法性校验,避免崩溃:
Data(std::string data) { rapidjson::Document document; // 保存解析结果,检查是否成功 rapidjson::ParseResult parse_result = document.Parse(data.c_str()); if (!parse_result || !document.IsObject()) { std::cerr << "JSON解析失败或不是Object类型: " << data << std::endl; std::cerr << "错误码: " << parse_result.Code() << ", 错误位置: " << parse_result.Offset() << std::endl; return; } // 逐个检查成员是否存在且类型正确,避免访问非法成员 if (document.HasMember("rotation") && document["rotation"].IsString()) { rotation_ = std::stof(document["rotation"].GetString()); } if (document.HasMember("size") && document["size"].IsString()) { size_ = std::stof(document["size"].GetString()); } if (document.HasMember("collide") && document["collide"].IsString()) { collide_ = std::stoi(document["collide"].GetString()); } if (document.HasMember("lifePoint") && document["lifePoint"].IsString()) { lifePoint_ = std::stoi(document["lifePoint"].GetString()); } if (document.HasMember("id") && document["id"].IsString()) { id_ = std::stoi(document["id"].GetString()); } if (document.HasMember("type") && document["type"].IsString()) { type_ = std::stoi(document["type"].GetString()); } if (document.HasMember("pos.x") && document["pos.x"].IsString()) { pos_.first = std::stof(document["pos.x"].GetString()); } if (document.HasMember("pos.y") && document["pos.y"].IsString()) { pos_.second = std::stof(document["pos.y"].GetString()); } };
3. 优化JSON生成格式(可选但推荐)
你当前生成的JSON中,数值都是字符串类型(比如"1.000000"),这不符合JSON的最佳实践,也增加了解析复杂度。建议用RapidJSON的Writer直接生成数值类型的JSON:
std::string getJson() { rapidjson::StringBuffer buffer; rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); writer.StartObject(); writer.Key("rotation"); writer.Double(rotation_); writer.Key("size"); writer.Double(size_); writer.Key("collide"); writer.Int(collide_); writer.Key("lifePoint"); writer.Int(lifePoint_); writer.Key("id"); writer.Int(id_); writer.Key("type"); writer.Int(type_); writer.Key("pos.x"); writer.Double(pos_.first); writer.Key("pos.y"); writer.Double(pos_.second); writer.EndObject(); return buffer.GetString(); };
这样生成的JSON中数值是原生Number类型,解析时可以直接用GetFloat()/GetInt(),不需要字符串转换,更高效也更安全。
总结
先修复UDP接收的字符串截取问题,这是导致断言失败的直接原因;再增加解析校验,让程序更健壮;最后优化JSON生成格式,遵循规范。按这个步骤修改后,你的程序应该就能正常运行了。
内容的提问来源于stack exchange,提问作者Gancar_d

