diff --git a/apriltag/src/main/native/cpp/AprilTag.cpp b/apriltag/src/main/native/cpp/AprilTag.cpp index adcbe68fb5..a14d9784a8 100644 --- a/apriltag/src/main/native/cpp/AprilTag.cpp +++ b/apriltag/src/main/native/cpp/AprilTag.cpp @@ -52,10 +52,10 @@ bool AprilTag::Generate16h5AprilTagImage(wpi::util::RawFrame* frame, int id) { } void wpi::apriltag::to_json(wpi::util::json& json, const AprilTag& apriltag) { - json = wpi::util::json{{"ID", apriltag.ID}, {"pose", apriltag.pose}}; + json = wpi::util::json::object("ID", apriltag.ID, "pose", apriltag.pose); } void wpi::apriltag::from_json(const wpi::util::json& json, AprilTag& apriltag) { - apriltag.ID = json.at("ID").get(); + apriltag.ID = json.at("ID").get_int(); apriltag.pose = json.at("pose").get(); } diff --git a/apriltag/src/main/native/cpp/AprilTagFieldLayout.cpp b/apriltag/src/main/native/cpp/AprilTagFieldLayout.cpp index 166ab3b883..567ebe6bdd 100644 --- a/apriltag/src/main/native/cpp/AprilTagFieldLayout.cpp +++ b/apriltag/src/main/native/cpp/AprilTagFieldLayout.cpp @@ -22,16 +22,16 @@ AprilTagFieldLayout::AprilTagFieldLayout(std::string_view path) { throw std::runtime_error(fmt::format("Cannot open file: {}", path)); } - wpi::util::json json = - wpi::util::json::parse(fileBuffer.value()->GetCharBuffer()); + auto buf = fileBuffer.value()->GetCharBuffer(); + auto json = wpi::util::json::parse_or_throw({buf.data(), buf.size()}); - for (const auto& tag : json.at("tags").get>()) { + for (auto&& jtag : json.at("tags").get_array()) { + auto tag = jtag.get(); m_apriltags[tag.ID] = tag; } - m_fieldWidth = - wpi::units::meter_t{json.at("field").at("width").get()}; + m_fieldWidth = wpi::units::meter_t{json.at("field").at("width").get_number()}; m_fieldLength = - wpi::units::meter_t{json.at("field").at("length").get()}; + wpi::units::meter_t{json.at("field").at("length").get_number()}; } AprilTagFieldLayout::AprilTagFieldLayout(std::vector apriltags, @@ -113,23 +113,24 @@ void wpi::apriltag::to_json(wpi::util::json& json, tagVector.push_back(pair.second); } - json = wpi::util::json{{"field", - {{"length", layout.m_fieldLength.value()}, - {"width", layout.m_fieldWidth.value()}}}, - {"tags", tagVector}}; + auto field = wpi::util::json::object("length", layout.m_fieldLength.value(), + "width", layout.m_fieldWidth.value()); + json = wpi::util::json::object("field", std::move(field), "tags", + std::move(tagVector)); } void wpi::apriltag::from_json(const wpi::util::json& json, AprilTagFieldLayout& layout) { layout.m_apriltags.clear(); - for (const auto& tag : json.at("tags").get>()) { + for (auto&& jtag : json.at("tags").get_array()) { + auto tag = jtag.get(); layout.m_apriltags[tag.ID] = tag; } layout.m_fieldLength = - wpi::units::meter_t{json.at("field").at("length").get()}; + wpi::units::meter_t{json.at("field").at("length").get_number()}; layout.m_fieldWidth = - wpi::units::meter_t{json.at("field").at("width").get()}; + wpi::units::meter_t{json.at("field").at("width").get_number()}; } // Use namespace declaration for forward declaration @@ -174,6 +175,6 @@ AprilTagFieldLayout AprilTagFieldLayout::LoadField(AprilTagField field) { throw std::invalid_argument("Invalid Field"); } - wpi::util::json json = wpi::util::json::parse(fieldString); + wpi::util::json json = wpi::util::json::parse_or_throw(fieldString); return json.get(); } diff --git a/apriltag/src/main/native/include/wpi/apriltag/AprilTag.hpp b/apriltag/src/main/native/include/wpi/apriltag/AprilTag.hpp index 87384cef07..3ad4fa406b 100644 --- a/apriltag/src/main/native/include/wpi/apriltag/AprilTag.hpp +++ b/apriltag/src/main/native/include/wpi/apriltag/AprilTag.hpp @@ -7,7 +7,10 @@ #include "wpi/math/geometry/Pose3d.hpp" #include "wpi/util/RawFrame.hpp" #include "wpi/util/SymbolExports.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpi::apriltag { diff --git a/apriltag/src/main/native/include/wpi/apriltag/AprilTagFieldLayout.hpp b/apriltag/src/main/native/include/wpi/apriltag/AprilTagFieldLayout.hpp index 4b6548f26c..810cc63cc8 100644 --- a/apriltag/src/main/native/include/wpi/apriltag/AprilTagFieldLayout.hpp +++ b/apriltag/src/main/native/include/wpi/apriltag/AprilTagFieldLayout.hpp @@ -14,7 +14,10 @@ #include "wpi/math/geometry/Pose3d.hpp" #include "wpi/units/length.hpp" #include "wpi/util/SymbolExports.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpi::apriltag { /** diff --git a/cameraserver/multiCameraServer/src/main/native/cpp/main.cpp b/cameraserver/multiCameraServer/src/main/native/cpp/main.cpp index d37982a819..1018fc0007 100644 --- a/cameraserver/multiCameraServer/src/main/native/cpp/main.cpp +++ b/cameraserver/multiCameraServer/src/main/native/cpp/main.cpp @@ -69,8 +69,8 @@ bool ReadCameraConfig(const wpi::util::json& config) { // name try { - c.name = config.at("name").get(); - } catch (const wpi::util::json::exception& e) { + c.name = config.at("name").get_string(); + } catch (const std::logic_error& e) { wpi::util::print(stderr, "config error in '{}': could not read camera name: {}\n", configFile, e.what()); @@ -79,8 +79,8 @@ bool ReadCameraConfig(const wpi::util::json& config) { // path try { - c.path = config.at("path").get(); - } catch (const wpi::util::json::exception& e) { + c.path = config.at("path").get_string(); + } catch (const std::logic_error& e) { wpi::util::print( stderr, "config error in '{}': camera '{}': could not read path: {}\n", configFile, c.name, e.what()); @@ -103,17 +103,16 @@ bool ReadConfig() { } // parse file - wpi::util::json j; - try { - j = wpi::util::json::parse(fileBuffer.value()->GetCharBuffer()); - } catch (const wpi::util::json::parse_error& e) { - wpi::util::print(stderr, "config error in '{}': byte {}: {}\n", configFile, - e.byte, e.what()); + auto buf = fileBuffer.value()->GetCharBuffer(); + auto j = wpi::util::json::parse({buf.data(), buf.size()}); + if (!j) { + wpi::util::print(stderr, "config error in '{}': {}\n", configFile, + j.error()); return false; } // top level must be an object - if (!j.is_object()) { + if (!j->is_object()) { wpi::util::print(stderr, "config error in '{}': must be JSON object\n", configFile); return false; @@ -121,8 +120,8 @@ bool ReadConfig() { // team number try { - team = j.at("team").get(); - } catch (const wpi::util::json::exception& e) { + team = j->at("team").get_int(); + } catch (const std::logic_error& e) { wpi::util::print(stderr, "config error in '{}': could not read team number: {}\n", configFile, e.what()); @@ -130,9 +129,9 @@ bool ReadConfig() { } // ntmode (optional) - if (j.count("ntmode") != 0) { + if (auto ntmode = j->lookup("ntmode")) { try { - auto str = j.at("ntmode").get(); + auto str = ntmode->get_string(); if (wpi::util::equals_lower(str, "client")) { server = false; } else if (wpi::util::equals_lower(str, "server")) { @@ -143,7 +142,7 @@ bool ReadConfig() { "config error in '{}': could not understand ntmode value '{}'\n", configFile, str); } - } catch (const wpi::util::json::exception& e) { + } catch (const std::logic_error& e) { wpi::util::print(stderr, "config error in '{}': could not read ntmode: {}\n", configFile, e.what()); @@ -152,12 +151,12 @@ bool ReadConfig() { // cameras try { - for (auto&& camera : j.at("cameras")) { + for (auto&& camera : j->at("cameras").get_array()) { if (!ReadCameraConfig(camera)) { return false; } } - } catch (const wpi::util::json::exception& e) { + } catch (const std::logic_error& e) { wpi::util::print(stderr, "config error in '{}': could not read cameras: {}\n", configFile, e.what()); diff --git a/cscore/src/main/native/cpp/PropertyContainer.cpp b/cscore/src/main/native/cpp/PropertyContainer.cpp index d57471b8ed..e609c5905e 100644 --- a/cscore/src/main/native/cpp/PropertyContainer.cpp +++ b/cscore/src/main/native/cpp/PropertyContainer.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include "wpi/util/Logger.hpp" @@ -244,11 +245,11 @@ bool PropertyContainer::SetPropertiesJson(const wpi::util::json& config, wpi::util::Logger& logger, std::string_view logName, CS_Status* status) { - for (auto&& prop : config) { + for (auto&& prop : config.get_array()) { std::string name; try { - name = prop.at("name").get(); - } catch (const wpi::util::json::exception& e) { + name = prop.at("name").get_string(); + } catch (const std::logic_error& e) { WPI_WARNING(logger, "{}: SetConfigJson: could not read property name: {}", logName, e.what()); continue; @@ -257,22 +258,22 @@ bool PropertyContainer::SetPropertiesJson(const wpi::util::json& config, try { auto& v = prop.at("value"); if (v.is_string()) { - std::string val = v.get(); + std::string val = v.get_string(); WPI_INFO(logger, "{}: SetConfigJson: setting property '{}' to '{}'", logName, name, val); SetStringProperty(n, val, status); - } else if (v.is_boolean()) { - bool val = v.get(); + } else if (v.is_bool()) { + bool val = v.get_bool(); WPI_INFO(logger, "{}: SetConfigJson: setting property '{}' to {}", logName, name, val); SetProperty(n, val, status); } else { - int val = v.get(); + int val = v.get_int(); WPI_INFO(logger, "{}: SetConfigJson: setting property '{}' to {}", logName, name, val); SetProperty(n, val, status); } - } catch (const wpi::util::json::exception& e) { + } catch (const std::logic_error& e) { WPI_WARNING(logger, "{}: SetConfigJson: could not read property value: {}", logName, e.what()); @@ -289,22 +290,22 @@ wpi::util::json PropertyContainer::GetPropertiesJsonObject(CS_Status* status) { for (int p : EnumerateProperties(propVec, status)) { wpi::util::json prop; wpi::util::SmallString<128> strBuf; - prop.emplace("name", GetPropertyName(p, strBuf, status)); + prop["name"] = GetPropertyName(p, strBuf, status); switch (GetPropertyKind(p)) { case CS_PROP_BOOLEAN: - prop.emplace("value", static_cast(GetProperty(p, status))); + prop["value"] = static_cast(GetProperty(p, status)); break; case CS_PROP_INTEGER: case CS_PROP_ENUM: - prop.emplace("value", GetProperty(p, status)); + prop["value"] = GetProperty(p, status); break; case CS_PROP_STRING: - prop.emplace("value", GetStringProperty(p, strBuf, status)); + prop["value"] = GetStringProperty(p, strBuf, status); break; default: continue; } - j.emplace_back(prop); + j.emplace_back(std::move(prop)); } return j; diff --git a/cscore/src/main/native/cpp/PropertyContainer.hpp b/cscore/src/main/native/cpp/PropertyContainer.hpp index 2461d44548..e142f94f06 100644 --- a/cscore/src/main/native/cpp/PropertyContainer.hpp +++ b/cscore/src/main/native/cpp/PropertyContainer.hpp @@ -15,13 +15,13 @@ #include "PropertyImpl.hpp" #include "wpi/cs/cscore_c.h" #include "wpi/util/StringMap.hpp" -#include "wpi/util/json_fwd.hpp" #include "wpi/util/mutex.hpp" namespace wpi::util { class Logger; template class SmallVectorImpl; +class json; } // namespace wpi::util namespace wpi::cs { diff --git a/cscore/src/main/native/cpp/SinkImpl.cpp b/cscore/src/main/native/cpp/SinkImpl.cpp index 81be1818ae..e1e8a01cfc 100644 --- a/cscore/src/main/native/cpp/SinkImpl.cpp +++ b/cscore/src/main/native/cpp/SinkImpl.cpp @@ -5,6 +5,7 @@ #include "SinkImpl.hpp" #include +#include #include "Instance.hpp" #include "Notifier.hpp" @@ -128,20 +129,18 @@ std::string_view SinkImpl::GetError( } bool SinkImpl::SetConfigJson(std::string_view config, CS_Status* status) { - wpi::util::json j; - try { - j = wpi::util::json::parse(config); - } catch (const wpi::util::json::parse_error& e) { - SWARNING("SetConfigJson: parse error at byte {}: {}", e.byte, e.what()); + auto j = wpi::util::json::parse(config); + if (!j) { + SWARNING("SetConfigJson: parse error: {}", j.error()); *status = CS_PROPERTY_WRITE_FAILED; return false; } - return SetConfigJson(j, status); + return SetConfigJson(*j, status); } bool SinkImpl::SetConfigJson(const wpi::util::json& config, CS_Status* status) { - if (config.count("properties") != 0) { - SetPropertiesJson(config.at("properties"), m_logger, GetName(), status); + if (auto properties = config.lookup("properties")) { + SetPropertiesJson(*properties, m_logger, GetName(), status); } return true; @@ -150,7 +149,7 @@ bool SinkImpl::SetConfigJson(const wpi::util::json& config, CS_Status* status) { std::string SinkImpl::GetConfigJson(CS_Status* status) { std::string rv; wpi::util::raw_string_ostream os(rv); - GetConfigJsonObject(status).dump(os, 4); + GetConfigJsonObject(status).marshal(os, true, 4); os.flush(); return rv; } @@ -160,7 +159,7 @@ wpi::util::json SinkImpl::GetConfigJsonObject(CS_Status* status) { wpi::util::json props = GetPropertiesJsonObject(status); if (props.is_array()) { - j.emplace("properties", props); + j["properties"] = std::move(props); } return j; diff --git a/cscore/src/main/native/cpp/SinkImpl.hpp b/cscore/src/main/native/cpp/SinkImpl.hpp index 865420a1cd..ef35d5aa03 100644 --- a/cscore/src/main/native/cpp/SinkImpl.hpp +++ b/cscore/src/main/native/cpp/SinkImpl.hpp @@ -10,7 +10,10 @@ #include "SourceImpl.hpp" #include "wpi/util/Logger.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpi::cs { diff --git a/cscore/src/main/native/cpp/SourceImpl.cpp b/cscore/src/main/native/cpp/SourceImpl.cpp index 4d5df1565e..f1e9909aad 100644 --- a/cscore/src/main/native/cpp/SourceImpl.cpp +++ b/cscore/src/main/native/cpp/SourceImpl.cpp @@ -180,15 +180,13 @@ bool SourceImpl::SetFPS(int fps, CS_Status* status) { } bool SourceImpl::SetConfigJson(std::string_view config, CS_Status* status) { - wpi::util::json j; - try { - j = wpi::util::json::parse(config); - } catch (const wpi::util::json::parse_error& e) { - SWARNING("SetConfigJson: parse error at byte {}: {}", e.byte, e.what()); + auto j = wpi::util::json::parse(config); + if (!j) { + SWARNING("SetConfigJson: parse error: {}", j.error()); *status = CS_PROPERTY_WRITE_FAILED; return false; } - return SetConfigJson(j, status); + return SetConfigJson(*j, status); } bool SourceImpl::SetConfigJson(const wpi::util::json& config, @@ -196,9 +194,9 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config, VideoMode mode; // pixel format - if (config.count("pixel format") != 0) { + if (auto pixelFormat = config.lookup("pixel format")) { try { - auto str = config.at("pixel format").get(); + auto str = pixelFormat->get_string(); if (wpi::util::equals_lower(str, "mjpeg")) { mode.pixelFormat = wpi::util::PixelFormat::MJPEG; } else if (wpi::util::equals_lower(str, "yuyv")) { @@ -219,34 +217,34 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config, SWARNING("SetConfigJson: could not understand pixel format value '{}'", str); } - } catch (const wpi::util::json::exception& e) { + } catch (const std::logic_error& e) { SWARNING("SetConfigJson: could not read pixel format: {}", e.what()); } } // width - if (config.count("width") != 0) { + if (auto width = config.lookup("width")) { try { - mode.width = config.at("width").get(); - } catch (const wpi::util::json::exception& e) { + mode.width = width->get_int(); + } catch (const std::logic_error& e) { SWARNING("SetConfigJson: could not read width: {}", e.what()); } } // height - if (config.count("height") != 0) { + if (auto height = config.lookup("height")) { try { - mode.height = config.at("height").get(); - } catch (const wpi::util::json::exception& e) { + mode.height = height->get_int(); + } catch (const std::logic_error& e) { SWARNING("SetConfigJson: could not read height: {}", e.what()); } } // fps - if (config.count("fps") != 0) { + if (auto fps = config.lookup("fps")) { try { - mode.fps = config.at("fps").get(); - } catch (const wpi::util::json::exception& e) { + mode.fps = fps->get_int(); + } catch (const std::logic_error& e) { SWARNING("SetConfigJson: could not read fps: {}", e.what()); } } @@ -277,22 +275,21 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config, } // brightness - if (config.count("brightness") != 0) { + if (auto brightness = config.lookup("brightness")) { try { - int val = config.at("brightness").get(); + int val = brightness->get_int(); SINFO("SetConfigJson: setting brightness to {}", val); SetBrightness(val, status); - } catch (const wpi::util::json::exception& e) { + } catch (const std::logic_error& e) { SWARNING("SetConfigJson: could not read brightness: {}", e.what()); } } // white balance - if (config.count("white balance") != 0) { + if (auto whiteBalance = config.lookup("white balance")) { try { - auto& setting = config.at("white balance"); - if (setting.is_string()) { - auto str = setting.get(); + if (whiteBalance->is_string()) { + auto str = whiteBalance->get_string(); if (wpi::util::equals_lower(str, "auto")) { SINFO("SetConfigJson: setting white balance to {}", "auto"); SetWhiteBalanceAuto(status); @@ -305,21 +302,20 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config, str); } } else { - int val = setting.get(); + int val = whiteBalance->get_int(); SINFO("SetConfigJson: setting white balance to {}", val); SetWhiteBalanceManual(val, status); } - } catch (const wpi::util::json::exception& e) { + } catch (const std::logic_error& e) { SWARNING("SetConfigJson: could not read white balance: {}", e.what()); } } // exposure - if (config.count("exposure") != 0) { + if (auto exposure = config.lookup("exposure")) { try { - auto& setting = config.at("exposure"); - if (setting.is_string()) { - auto str = setting.get(); + if (exposure->is_string()) { + auto str = exposure->get_string(); if (wpi::util::equals_lower(str, "auto")) { SINFO("SetConfigJson: setting exposure to {}", "auto"); SetExposureAuto(status); @@ -331,18 +327,18 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config, str); } } else { - int val = setting.get(); + int val = exposure->get_int(); SINFO("SetConfigJson: setting exposure to {}", val); SetExposureManual(val, status); } - } catch (const wpi::util::json::exception& e) { + } catch (const std::logic_error& e) { SWARNING("SetConfigJson: could not read exposure: {}", e.what()); } } // properties - if (config.count("properties") != 0) { - SetPropertiesJson(config.at("properties"), m_logger, GetName(), status); + if (auto properties = config.lookup("properties")) { + SetPropertiesJson(*properties, m_logger, GetName(), status); } return true; @@ -351,8 +347,7 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config, std::string SourceImpl::GetConfigJson(CS_Status* status) { std::string rv; wpi::util::raw_string_ostream os(rv); - GetConfigJsonObject(status).dump(os, 4); - os.flush(); + GetConfigJsonObject(status).marshal(os, true, 4); return rv; } @@ -390,22 +385,22 @@ wpi::util::json SourceImpl::GetConfigJsonObject(CS_Status* status) { break; } if (!pixelFormat.empty()) { - j.emplace("pixel format", pixelFormat); + j["pixel format"] = pixelFormat; } // width if (m_mode.width != 0) { - j.emplace("width", m_mode.width); + j["width"] = m_mode.width; } // height if (m_mode.height != 0) { - j.emplace("height", m_mode.height); + j["height"] = m_mode.height; } // fps if (m_mode.fps != 0) { - j.emplace("fps", m_mode.fps); + j["fps"] = m_mode.fps; } // TODO: output brightness, white balance, and exposure? @@ -413,7 +408,7 @@ wpi::util::json SourceImpl::GetConfigJsonObject(CS_Status* status) { // properties wpi::util::json props = GetPropertiesJsonObject(status); if (props.is_array()) { - j.emplace("properties", props); + j["properties"] = std::move(props); } return j; diff --git a/cscore/src/main/native/cpp/SourceImpl.hpp b/cscore/src/main/native/cpp/SourceImpl.hpp index 13e548390a..e441db2972 100644 --- a/cscore/src/main/native/cpp/SourceImpl.hpp +++ b/cscore/src/main/native/cpp/SourceImpl.hpp @@ -20,9 +20,12 @@ #include "wpi/util/PixelFormat.hpp" #include "wpi/util/RawFrame.h" #include "wpi/util/condition_variable.hpp" -#include "wpi/util/json_fwd.hpp" #include "wpi/util/mutex.hpp" +namespace wpi::util { +class json; +} // namespace wpi::util + namespace wpi::cs { class Notifier; diff --git a/cscore/src/main/native/include/wpi/cs/VideoSink.hpp b/cscore/src/main/native/include/wpi/cs/VideoSink.hpp index 93a2b1e8e3..98eeaf57d4 100644 --- a/cscore/src/main/native/include/wpi/cs/VideoSink.hpp +++ b/cscore/src/main/native/include/wpi/cs/VideoSink.hpp @@ -12,7 +12,10 @@ #include "wpi/cs/VideoProperty.hpp" #include "wpi/cs/VideoSource.hpp" #include "wpi/cs/cscore_cpp.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpi::cs { diff --git a/cscore/src/main/native/include/wpi/cs/cscore_cpp.hpp b/cscore/src/main/native/include/wpi/cs/cscore_cpp.hpp index d581ea2857..7d206068de 100644 --- a/cscore/src/main/native/include/wpi/cs/cscore_cpp.hpp +++ b/cscore/src/main/native/include/wpi/cs/cscore_cpp.hpp @@ -18,7 +18,6 @@ #include "wpi/cs/cscore_c.h" #include "wpi/util/PixelFormat.hpp" #include "wpi/util/SmallVector.hpp" -#include "wpi/util/json_fwd.hpp" #ifdef _WIN32 // Disable uninitialized variable warnings @@ -26,6 +25,10 @@ #pragma warning(disable : 26495) #endif +namespace wpi::util { +class json; +} // namespace wpi::util + /** CameraServer (cscore) namespace */ namespace wpi::cs { diff --git a/glass/src/lib/native/cpp/Context.cpp b/glass/src/lib/native/cpp/Context.cpp index 3988577f16..7b7cbd7599 100644 --- a/glass/src/lib/native/cpp/Context.cpp +++ b/glass/src/lib/native/cpp/Context.cpp @@ -67,63 +67,55 @@ static bool JsonToWindow(const wpi::util::json& jfile, const char* filename) { std::string iniStr; wpi::util::raw_string_ostream ini{iniStr}; - for (auto&& jsection : jfile.items()) { - if (jsection.key() == "Docking") { + for (auto&& [sect_key, sect_value] : jfile.get_object()) { + if (sect_key == "Docking") { continue; } - if (!jsection.value().is_object()) { - ImGui::LogText("%s section %s is not object", filename, - jsection.key().c_str()); + if (!sect_value.is_object()) { + ImGui::LogText("%s section %s is not object", filename, sect_key.c_str()); return false; } - for (auto&& jsubsection : jsection.value().items()) { - if (!jsubsection.value().is_object()) { + for (auto&& [sub_key, sub_value] : sect_value.get_object()) { + if (!sub_value.is_object()) { ImGui::LogText("%s section %s subsection %s is not object", filename, - jsection.key().c_str(), jsubsection.key().c_str()); + sect_key.c_str(), sub_key.c_str()); return false; } - ini << '[' << jsection.key() << "][" << jsubsection.key() << "]\n"; - for (auto&& jkv : jsubsection.value().items()) { - try { - auto& value = jkv.value().get_ref(); - ini << jkv.key() << '=' << value << "\n"; - } catch (wpi::util::json::exception&) { + ini << '[' << sect_key << "][" << sub_key << "]\n"; + for (auto&& [key, value] : sub_value.get_object()) { + if (!value.is_string()) { ImGui::LogText("%s section %s subsection %s value %s is not string", - filename, jsection.key().c_str(), - jsubsection.key().c_str(), jkv.key().c_str()); + filename, sect_key.c_str(), sub_key.c_str(), + key.c_str()); return false; } + ini << key << '=' << value.get_string() << "\n"; } ini << '\n'; } } // emit Docking section last - auto docking = jfile.find("Docking"); - if (docking != jfile.end()) { - for (auto&& jsubsection : docking->items()) { - if (!jsubsection.value().is_array()) { + if (auto docking = jfile.lookup("Docking")) { + for (auto&& [sub_key, sub_value] : docking->get_object()) { + if (!sub_value.is_array()) { ImGui::LogText("%s section %s subsection %s is not array", filename, - "Docking", jsubsection.key().c_str()); + "Docking", sub_key.c_str()); return false; } - ini << "[Docking][" << jsubsection.key() << "]\n"; - for (auto&& jv : jsubsection.value()) { - try { - auto& value = jv.get_ref(); - ini << value << "\n"; - } catch (wpi::util::json::exception&) { + ini << "[Docking][" << sub_key << "]\n"; + for (auto&& value : sub_value.get_array()) { + if (!value.is_string()) { ImGui::LogText("%s section %s subsection %s value is not string", - filename, "Docking", jsubsection.key().c_str()); + filename, "Docking", sub_key.c_str()); return false; } + ini << value.get_string() << "\n"; } ini << '\n'; } } - ini.flush(); - ImGui::LoadIniSettingsFromMemory(iniStr.data(), iniStr.size()); return true; } @@ -135,12 +127,12 @@ static bool LoadWindowStorageImpl(const std::string& filename) { fileBuffer.error().message().c_str()); return false; } - try { - return JsonToWindow( - wpi::util::json::parse(fileBuffer.value()->GetCharBuffer()), - filename.c_str()); - } catch (wpi::util::json::parse_error& e) { - ImGui::LogText("Error loading %s: %s", filename.c_str(), e.what()); + auto buffer = fileBuffer.value()->GetCharBuffer(); + auto j = wpi::util::json::parse({buffer.data(), buffer.size()}); + if (j) { + return JsonToWindow(*j, filename.c_str()); + } else { + ImGui::LogText("Error loading %s: %s", filename.c_str(), j.error()); return false; } } @@ -154,12 +146,12 @@ static bool LoadStorageRootImpl(Context* ctx, const std::string& filename, return false; } auto [it, createdStorage] = ctx->storageRoots.try_emplace(rootName); - try { - it->second.FromJson( - wpi::util::json::parse(fileBuffer.value()->GetCharBuffer()), - filename.c_str()); - } catch (wpi::util::json::parse_error& e) { - ImGui::LogText("Error loading %s: %s", filename.c_str(), e.what()); + auto buffer = fileBuffer.value()->GetCharBuffer(); + auto j = wpi::util::json::parse({buffer.data(), buffer.size()}); + if (j) { + it->second.FromJson(*j, filename.c_str()); + } else { + ImGui::LogText("Error loading %s: %s", filename.c_str(), j.error()); if (createdStorage) { ctx->storageRoots.erase(it); } @@ -257,7 +249,7 @@ bool SaveWindowStorageImpl(const std::string& filename) { ec.message().c_str()); return false; } - WindowToJson().dump(os, 2); + WindowToJson().marshal(os, true, 2); os << '\n'; return true; } @@ -271,7 +263,7 @@ static bool SaveStorageRootImpl(Context* ctx, const std::string& filename, ec.message().c_str()); return false; } - storage.ToJson().dump(os, 2); + storage.ToJson().marshal(os, true, 2); os << '\n'; return true; } diff --git a/glass/src/lib/native/cpp/Storage.cpp b/glass/src/lib/native/cpp/Storage.cpp index a0cb6e8683..0e6257a1be 100644 --- a/glass/src/lib/native/cpp/Storage.cpp +++ b/glass/src/lib/native/cpp/Storage.cpp @@ -348,7 +348,7 @@ void Storage::EraseChildren() { static bool JsonArrayToStorage(Storage::Value* valuePtr, const wpi::util::json& jarr, const char* filename) { - auto& arr = jarr.get_ref(); + auto& arr = jarr.get_array(); if (arr.empty()) { ImGui::LogText("empty array in %s, ignoring", filename); return false; @@ -356,42 +356,45 @@ static bool JsonArrayToStorage(Storage::Value* valuePtr, // guess array type from first element switch (arr[0].type()) { - case wpi::util::json::value_t::boolean: + case wpi::util::json::Type::Bool: if (valuePtr->type != Storage::Value::kBoolArray) { valuePtr->Reset(Storage::Value::kBoolArray); valuePtr->boolArray = new std::vector(); valuePtr->boolArrayDefault = nullptr; } break; - case wpi::util::json::value_t::number_float: + case wpi::util::json::Type::Float: + case wpi::util::json::Type::Double: if (valuePtr->type != Storage::Value::kDoubleArray) { valuePtr->Reset(Storage::Value::kDoubleArray); valuePtr->doubleArray = new std::vector(); valuePtr->doubleArrayDefault = nullptr; } break; - case wpi::util::json::value_t::number_integer: - case wpi::util::json::value_t::number_unsigned: + case wpi::util::json::Type::Int: if (valuePtr->type != Storage::Value::kInt64Array) { valuePtr->Reset(Storage::Value::kInt64Array); valuePtr->int64Array = new std::vector(); valuePtr->int64ArrayDefault = nullptr; } break; - case wpi::util::json::value_t::string: + case wpi::util::json::Type::Uint: + ImGui::LogText("too large of integer in %s, ignoring", filename); + return false; + case wpi::util::json::Type::String: if (valuePtr->type != Storage::Value::kStringArray) { valuePtr->Reset(Storage::Value::kStringArray); valuePtr->stringArray = new std::vector(); valuePtr->stringArrayDefault = nullptr; } break; - case wpi::util::json::value_t::object: + case wpi::util::json::Type::Object: if (valuePtr->type != Storage::Value::kChildArray) { valuePtr->Reset(Storage::Value::kChildArray); valuePtr->childArray = new std::vector>(); } break; - case wpi::util::json::value_t::array: + case wpi::util::json::Type::Array: ImGui::LogText("nested array in %s, ignoring", filename); return false; default: @@ -402,47 +405,47 @@ static bool JsonArrayToStorage(Storage::Value* valuePtr, // loop over array to store elements for (auto jvalue : arr) { switch (jvalue.type()) { - case wpi::util::json::value_t::boolean: + case wpi::util::json::Type::Bool: if (valuePtr->type == Storage::Value::kBoolArray) { - valuePtr->boolArray->push_back(jvalue.get()); + valuePtr->boolArray->push_back(jvalue.get_bool()); } else { goto error; } break; - case wpi::util::json::value_t::number_float: + case wpi::util::json::Type::Float: if (valuePtr->type == Storage::Value::kDoubleArray) { - valuePtr->doubleArray->push_back(jvalue.get()); + valuePtr->doubleArray->push_back(jvalue.get_float()); } else { goto error; } break; - case wpi::util::json::value_t::number_integer: + case wpi::util::json::Type::Double: + if (valuePtr->type == Storage::Value::kDoubleArray) { + valuePtr->doubleArray->push_back(jvalue.get_double()); + } else { + goto error; + } + break; + case wpi::util::json::Type::Int: if (valuePtr->type == Storage::Value::kInt64Array) { - valuePtr->int64Array->push_back(jvalue.get()); + valuePtr->int64Array->push_back(jvalue.get_int()); } else if (valuePtr->type == Storage::Value::kDoubleArray) { - valuePtr->doubleArray->push_back(jvalue.get()); + valuePtr->doubleArray->push_back(jvalue.get_int()); } else { goto error; } break; - case wpi::util::json::value_t::number_unsigned: - if (valuePtr->type == Storage::Value::kInt64Array) { - valuePtr->int64Array->push_back(jvalue.get()); - } else if (valuePtr->type == Storage::Value::kDoubleArray) { - valuePtr->doubleArray->push_back(jvalue.get()); - } else { - goto error; - } - break; - case wpi::util::json::value_t::string: + case wpi::util::json::Type::Uint: + ImGui::LogText("too large of integer in %s, ignoring", filename); + return false; + case wpi::util::json::Type::String: if (valuePtr->type == Storage::Value::kStringArray) { - valuePtr->stringArray->emplace_back( - jvalue.get_ref()); + valuePtr->stringArray->emplace_back(jvalue.get_string()); } else { goto error; } break; - case wpi::util::json::value_t::object: + case wpi::util::json::Type::Object: if (valuePtr->type == Storage::Value::kChildArray) { valuePtr->childArray->emplace_back(std::make_unique()); valuePtr->childArray->back()->FromJson(jvalue, filename); @@ -450,7 +453,7 @@ static bool JsonArrayToStorage(Storage::Value* valuePtr, goto error; } break; - case wpi::util::json::value_t::array: + case wpi::util::json::Type::Array: ImGui::LogText("nested array in %s, ignoring", filename); return false; default: @@ -474,53 +477,52 @@ bool Storage::FromJson(const wpi::util::json& json, const char* filename) { ImGui::LogText("non-object in %s", filename); return false; } - for (auto&& jkv : json.items()) { - auto& valuePtr = m_values[jkv.key()]; + for (auto&& [key, jvalue] : json.get_object()) { + auto& valuePtr = m_values[key]; bool created = false; if (!valuePtr) { valuePtr = std::make_unique(); created = true; } - auto& jvalue = jkv.value(); switch (jvalue.type()) { - case wpi::util::json::value_t::boolean: + case wpi::util::json::Type::Bool: valuePtr->Reset(Value::kBool); - valuePtr->boolVal = jvalue.get(); + valuePtr->boolVal = jvalue.get_bool(); break; - case wpi::util::json::value_t::number_float: + case wpi::util::json::Type::Float: valuePtr->Reset(Value::kDouble); - valuePtr->doubleVal = jvalue.get(); + valuePtr->doubleVal = jvalue.get_float(); break; - case wpi::util::json::value_t::number_integer: + case wpi::util::json::Type::Double: + valuePtr->Reset(Value::kDouble); + valuePtr->doubleVal = jvalue.get_double(); + break; + case wpi::util::json::Type::Int: valuePtr->Reset(Value::kInt64); - valuePtr->int64Val = jvalue.get(); + valuePtr->int64Val = jvalue.get_int(); break; - case wpi::util::json::value_t::number_unsigned: - valuePtr->Reset(Value::kInt64); - valuePtr->int64Val = jvalue.get(); - break; - case wpi::util::json::value_t::string: + case wpi::util::json::Type::String: valuePtr->Reset(Value::kString); - valuePtr->stringVal = jvalue.get_ref(); + valuePtr->stringVal = jvalue.get_string(); break; - case wpi::util::json::value_t::object: + case wpi::util::json::Type::Object: if (valuePtr->type != Value::kChild) { valuePtr->Reset(Value::kChild); valuePtr->child = new Storage; } valuePtr->child->FromJson(jvalue, filename); // recurse break; - case wpi::util::json::value_t::array: + case wpi::util::json::Type::Array: if (!JsonArrayToStorage(valuePtr.get(), jvalue, filename)) { if (created) { - m_values.erase(jkv.key()); + m_values.erase(key); } } break; default: ImGui::LogText("null value in %s, ignoring", filename); if (created) { - m_values.erase(jkv.key()); + m_values.erase(key); } break; } @@ -545,8 +547,9 @@ wpi::util::json StorageToJsonArray>( jarr.emplace_back(v->ToJson()); } // remove any trailing empty items - while (!jarr.empty() && jarr.back().empty()) { - jarr.get_ref().pop_back(); + auto& jarrArr = jarr.get_array(); + while (!jarrArr.empty() && jarrArr.back().empty()) { + jarrArr.pop_back(); } return jarr; } @@ -602,7 +605,7 @@ wpi::util::json Storage::ToJson() const { default: continue; } - j.emplace(kv.first, std::move(jelem)); + j[kv.first] = std::move(jelem); } return j; } diff --git a/glass/src/lib/native/cpp/other/Field2D.cpp b/glass/src/lib/native/cpp/other/Field2D.cpp index f67577bf69..74d51857d3 100644 --- a/glass/src/lib/native/cpp/other/Field2D.cpp +++ b/glass/src/lib/native/cpp/other/Field2D.cpp @@ -451,16 +451,14 @@ void FieldInfo::LoadImage() { bool FieldInfo::LoadJson(std::span is, std::string_view filename) { // parse file - wpi::util::json j; - try { - j = wpi::util::json::parse(is); - } catch (const wpi::util::json::parse_error& e) { - wpi::util::print(stderr, "GUI: JSON: could not parse: {}\n", e.what()); + auto j = wpi::util::json::parse({is.data(), is.size()}); + if (!j) { + wpi::util::print(stderr, "GUI: JSON: could not parse: {}\n", j.error()); return false; } // top level must be an object - if (!j.is_object()) { + if (!j->is_object()) { std::fputs("GUI: JSON: does not contain a top object\n", stderr); return false; } @@ -468,8 +466,8 @@ bool FieldInfo::LoadJson(std::span is, std::string_view filename) { // image filename std::string image; try { - image = j.at("field-image").get(); - } catch (const wpi::util::json::exception& e) { + image = j->at("field-image").get_string(); + } catch (const std::logic_error& e) { wpi::util::print(stderr, "GUI: JSON: could not read field-image: {}\n", e.what()); return false; @@ -478,11 +476,11 @@ bool FieldInfo::LoadJson(std::span is, std::string_view filename) { // corners int top, left, bottom, right; try { - top = j.at("field-corners").at("top-left").at(1).get(); - left = j.at("field-corners").at("top-left").at(0).get(); - bottom = j.at("field-corners").at("bottom-right").at(1).get(); - right = j.at("field-corners").at("bottom-right").at(0).get(); - } catch (const wpi::util::json::exception& e) { + top = j->at("field-corners").at("top-left").at(1).get_int(); + left = j->at("field-corners").at("top-left").at(0).get_int(); + bottom = j->at("field-corners").at("bottom-right").at(1).get_int(); + right = j->at("field-corners").at("bottom-right").at(0).get_int(); + } catch (const std::logic_error& e) { wpi::util::print(stderr, "GUI: JSON: could not read field-corners: {}\n", e.what()); return false; @@ -492,9 +490,9 @@ bool FieldInfo::LoadJson(std::span is, std::string_view filename) { float width; float height; try { - width = j.at("field-size").at(0).get(); - height = j.at("field-size").at(1).get(); - } catch (const wpi::util::json::exception& e) { + width = j->at("field-size").at(0).get_float(); + height = j->at("field-size").at(1).get_float(); + } catch (const std::logic_error& e) { wpi::util::print(stderr, "GUI: JSON: could not read field-size: {}\n", e.what()); return false; @@ -503,8 +501,8 @@ bool FieldInfo::LoadJson(std::span is, std::string_view filename) { // units for size std::string unit; try { - unit = j.at("field-unit").get(); - } catch (const wpi::util::json::exception& e) { + unit = j->at("field-unit").get_string(); + } catch (const std::logic_error& e) { wpi::util::print(stderr, "GUI: JSON: could not read field-unit: {}\n", e.what()); return false; diff --git a/glass/src/lib/native/include/wpi/glass/Storage.hpp b/glass/src/lib/native/include/wpi/glass/Storage.hpp index 9ac50d5e8a..0cf3d5fb6c 100644 --- a/glass/src/lib/native/include/wpi/glass/Storage.hpp +++ b/glass/src/lib/native/include/wpi/glass/Storage.hpp @@ -16,7 +16,10 @@ #include "wpi/util/StringMap.hpp" #include "wpi/util/iterator_range.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpi::glass { diff --git a/glass/src/libnt/native/cpp/NTStringChooser.cpp b/glass/src/libnt/native/cpp/NTStringChooser.cpp index 2308c6327a..c1e536fdf8 100644 --- a/glass/src/libnt/native/cpp/NTStringChooser.cpp +++ b/glass/src/libnt/native/cpp/NTStringChooser.cpp @@ -24,7 +24,7 @@ NTStringChooserModel::NTStringChooserModel(wpi::nt::NetworkTableInstance inst, .Subscribe("")}, m_selectedPub{m_inst.GetStringTopic(fmt::format("{}/selected", path)) .PublishEx(wpi::nt::StringTopic::TYPE_STRING, - {{"retained", true}})}, + wpi::util::json::object("retained", true))}, m_active{ m_inst.GetStringTopic(fmt::format("{}/active", path)).Subscribe("")}, m_options{m_inst.GetStringArrayTopic(fmt::format("{}/options", path)) diff --git a/glass/src/libnt/native/cpp/NetworkTables.cpp b/glass/src/libnt/native/cpp/NetworkTables.cpp index 07fa47d0f6..ec78b6cea6 100644 --- a/glass/src/libnt/native/cpp/NetworkTables.cpp +++ b/glass/src/libnt/native/cpp/NetworkTables.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -110,18 +109,16 @@ void NetworkTablesModel::Entry::UpdateInfo(wpi::nt::TopicInfo&& info_) { properties = info.GetProperties(); persistent = false; - auto it = properties.find("persistent"); - if (it != properties.end()) { - if (auto v = it->get_ptr()) { - persistent = *v; + if (auto prop = properties.lookup("persistent")) { + if (prop->is_bool()) { + persistent = prop->get_bool(); } } retained = false; - it = properties.find("retained"); - if (it != properties.end()) { - if (auto v = it->get_ptr()) { - retained = *v; + if (auto prop = properties.lookup("retained")) { + if (prop->is_bool()) { + retained = prop->get_bool(); } } } @@ -642,7 +639,7 @@ static void UpdateJsonValueSource(NetworkTablesModel& model, const wpi::util::json& j, std::string_view name, int64_t time) { switch (j.type()) { - case wpi::util::json::value_t::object: { + case wpi::util::json::Type::Object: { if (!out->valueChildrenMap) { out->valueChildren.clear(); out->valueChildrenMap = true; @@ -652,19 +649,19 @@ static void UpdateJsonValueSource(NetworkTablesModel& model, elems[out->valueChildren[i].name] = i; } bool added = false; - for (auto&& kv : j.items()) { - auto it = elems.find(kv.key()); + for (auto&& [key, value] : j.get_object()) { + auto it = elems.find(key); if (it != elems.end()) { auto& child = out->valueChildren[it->second]; - UpdateJsonValueSource(model, &child, kv.value(), child.path, time); + UpdateJsonValueSource(model, &child, value, child.path, time); elems.erase(it); } else { added = true; out->valueChildren.emplace_back(); auto& child = out->valueChildren.back(); - child.name = kv.key(); + child.name = key; child.path = fmt::format("{}/{}", name, child.name); - UpdateJsonValueSource(model, &child, kv.value(), child.path, time); + UpdateJsonValueSource(model, &child, value, child.path, time); } } // erase unmatched keys @@ -680,12 +677,13 @@ static void UpdateJsonValueSource(NetworkTablesModel& model, } break; } - case wpi::util::json::value_t::array: { + case wpi::util::json::Type::Array: { + auto& arr = j.get_array(); if (out->valueChildrenMap) { out->valueChildren.clear(); out->valueChildrenMap = false; } - out->valueChildren.resize(j.size()); + out->valueChildren.resize(arr.size()); unsigned int i = 0; for (auto&& child : out->valueChildren) { if (child.name.empty()) { @@ -693,29 +691,24 @@ static void UpdateJsonValueSource(NetworkTablesModel& model, child.path = fmt::format("{}{}", name, child.name); } // recurse - UpdateJsonValueSource(model, &child, j[i++], child.path, time); + UpdateJsonValueSource(model, &child, arr[i++], child.path, time); } break; } - case wpi::util::json::value_t::string: - out->value = - wpi::nt::Value::MakeString(j.get_ref(), time); + case wpi::util::json::Type::String: + out->value = wpi::nt::Value::MakeString(j.get_string(), time); out->UpdateFromValue(model, name, ""); break; - case wpi::util::json::value_t::boolean: - out->value = wpi::nt::Value::MakeBoolean(j.get(), time); + case wpi::util::json::Type::Bool: + out->value = wpi::nt::Value::MakeBoolean(j.get_bool(), time); out->UpdateFromValue(model, name, ""); break; - case wpi::util::json::value_t::number_integer: - out->value = wpi::nt::Value::MakeInteger(j.get(), time); + case wpi::util::json::Type::Int: + out->value = wpi::nt::Value::MakeInteger(j.get_int(), time); out->UpdateFromValue(model, name, ""); break; - case wpi::util::json::value_t::number_unsigned: - out->value = wpi::nt::Value::MakeInteger(j.get(), time); - out->UpdateFromValue(model, name, ""); - break; - case wpi::util::json::value_t::number_float: - out->value = wpi::nt::Value::MakeDouble(j.get(), time); + case wpi::util::json::Type::Float: + out->value = wpi::nt::Value::MakeDouble(j.get_double(), time); out->UpdateFromValue(model, name, ""); break; default: @@ -877,12 +870,8 @@ void NetworkTablesModel::ValueSource::UpdateFromValue( } case NT_STRING: if (typeStr == "json") { - try { - UpdateJsonValueSource(model, this, - wpi::util::json::parse(value.GetString()), name, - value.last_change()); - } catch (wpi::util::json::exception&) { - // ignore + if (auto j = wpi::util::json::parse(value.GetString())) { + UpdateJsonValueSource(model, this, *j, name, value.last_change()); } } else { valueChildren.clear(); diff --git a/ntcore/src/generate/main/native/include/wpi/nt/Topic.hpp.jinja b/ntcore/src/generate/main/native/include/wpi/nt/Topic.hpp.jinja index 71133ef0bd..6f97e1b1ad 100644 --- a/ntcore/src/generate/main/native/include/wpi/nt/Topic.hpp.jinja +++ b/ntcore/src/generate/main/native/include/wpi/nt/Topic.hpp.jinja @@ -13,8 +13,6 @@ #include #include -#include "wpi/util/json_fwd.hpp" - #include "wpi/nt/NetworkTableType.hpp" #include "wpi/nt/Topic.hpp" #include "wpi/nt/ntcore_cpp.hpp" @@ -22,6 +20,7 @@ namespace wpi::util { template class SmallVectorImpl; +class json; } // namespace wpi namespace wpi::nt { diff --git a/ntcore/src/generated/main/native/include/wpi/nt/BooleanArrayTopic.hpp b/ntcore/src/generated/main/native/include/wpi/nt/BooleanArrayTopic.hpp index b9cc5bfe01..3bd6321de6 100644 --- a/ntcore/src/generated/main/native/include/wpi/nt/BooleanArrayTopic.hpp +++ b/ntcore/src/generated/main/native/include/wpi/nt/BooleanArrayTopic.hpp @@ -13,8 +13,6 @@ #include #include -#include "wpi/util/json_fwd.hpp" - #include "wpi/nt/NetworkTableType.hpp" #include "wpi/nt/Topic.hpp" #include "wpi/nt/ntcore_cpp.hpp" @@ -22,6 +20,7 @@ namespace wpi::util { template class SmallVectorImpl; +class json; } // namespace wpi namespace wpi::nt { diff --git a/ntcore/src/generated/main/native/include/wpi/nt/BooleanTopic.hpp b/ntcore/src/generated/main/native/include/wpi/nt/BooleanTopic.hpp index 5dff0f5d42..bf52684f12 100644 --- a/ntcore/src/generated/main/native/include/wpi/nt/BooleanTopic.hpp +++ b/ntcore/src/generated/main/native/include/wpi/nt/BooleanTopic.hpp @@ -13,8 +13,6 @@ #include #include -#include "wpi/util/json_fwd.hpp" - #include "wpi/nt/NetworkTableType.hpp" #include "wpi/nt/Topic.hpp" #include "wpi/nt/ntcore_cpp.hpp" @@ -22,6 +20,7 @@ namespace wpi::util { template class SmallVectorImpl; +class json; } // namespace wpi namespace wpi::nt { diff --git a/ntcore/src/generated/main/native/include/wpi/nt/DoubleArrayTopic.hpp b/ntcore/src/generated/main/native/include/wpi/nt/DoubleArrayTopic.hpp index 56b4ef356d..22bd4e8b56 100644 --- a/ntcore/src/generated/main/native/include/wpi/nt/DoubleArrayTopic.hpp +++ b/ntcore/src/generated/main/native/include/wpi/nt/DoubleArrayTopic.hpp @@ -13,8 +13,6 @@ #include #include -#include "wpi/util/json_fwd.hpp" - #include "wpi/nt/NetworkTableType.hpp" #include "wpi/nt/Topic.hpp" #include "wpi/nt/ntcore_cpp.hpp" @@ -22,6 +20,7 @@ namespace wpi::util { template class SmallVectorImpl; +class json; } // namespace wpi namespace wpi::nt { diff --git a/ntcore/src/generated/main/native/include/wpi/nt/DoubleTopic.hpp b/ntcore/src/generated/main/native/include/wpi/nt/DoubleTopic.hpp index 0339208128..c03f7594e9 100644 --- a/ntcore/src/generated/main/native/include/wpi/nt/DoubleTopic.hpp +++ b/ntcore/src/generated/main/native/include/wpi/nt/DoubleTopic.hpp @@ -13,8 +13,6 @@ #include #include -#include "wpi/util/json_fwd.hpp" - #include "wpi/nt/NetworkTableType.hpp" #include "wpi/nt/Topic.hpp" #include "wpi/nt/ntcore_cpp.hpp" @@ -22,6 +20,7 @@ namespace wpi::util { template class SmallVectorImpl; +class json; } // namespace wpi namespace wpi::nt { diff --git a/ntcore/src/generated/main/native/include/wpi/nt/FloatArrayTopic.hpp b/ntcore/src/generated/main/native/include/wpi/nt/FloatArrayTopic.hpp index 55ff731b9e..ebc8dc2dc4 100644 --- a/ntcore/src/generated/main/native/include/wpi/nt/FloatArrayTopic.hpp +++ b/ntcore/src/generated/main/native/include/wpi/nt/FloatArrayTopic.hpp @@ -13,8 +13,6 @@ #include #include -#include "wpi/util/json_fwd.hpp" - #include "wpi/nt/NetworkTableType.hpp" #include "wpi/nt/Topic.hpp" #include "wpi/nt/ntcore_cpp.hpp" @@ -22,6 +20,7 @@ namespace wpi::util { template class SmallVectorImpl; +class json; } // namespace wpi namespace wpi::nt { diff --git a/ntcore/src/generated/main/native/include/wpi/nt/FloatTopic.hpp b/ntcore/src/generated/main/native/include/wpi/nt/FloatTopic.hpp index 0adb178b1a..d8517b4114 100644 --- a/ntcore/src/generated/main/native/include/wpi/nt/FloatTopic.hpp +++ b/ntcore/src/generated/main/native/include/wpi/nt/FloatTopic.hpp @@ -13,8 +13,6 @@ #include #include -#include "wpi/util/json_fwd.hpp" - #include "wpi/nt/NetworkTableType.hpp" #include "wpi/nt/Topic.hpp" #include "wpi/nt/ntcore_cpp.hpp" @@ -22,6 +20,7 @@ namespace wpi::util { template class SmallVectorImpl; +class json; } // namespace wpi namespace wpi::nt { diff --git a/ntcore/src/generated/main/native/include/wpi/nt/IntegerArrayTopic.hpp b/ntcore/src/generated/main/native/include/wpi/nt/IntegerArrayTopic.hpp index 699a1a495d..4c06c65d6e 100644 --- a/ntcore/src/generated/main/native/include/wpi/nt/IntegerArrayTopic.hpp +++ b/ntcore/src/generated/main/native/include/wpi/nt/IntegerArrayTopic.hpp @@ -13,8 +13,6 @@ #include #include -#include "wpi/util/json_fwd.hpp" - #include "wpi/nt/NetworkTableType.hpp" #include "wpi/nt/Topic.hpp" #include "wpi/nt/ntcore_cpp.hpp" @@ -22,6 +20,7 @@ namespace wpi::util { template class SmallVectorImpl; +class json; } // namespace wpi namespace wpi::nt { diff --git a/ntcore/src/generated/main/native/include/wpi/nt/IntegerTopic.hpp b/ntcore/src/generated/main/native/include/wpi/nt/IntegerTopic.hpp index c9f3db5fe1..4b6058fc44 100644 --- a/ntcore/src/generated/main/native/include/wpi/nt/IntegerTopic.hpp +++ b/ntcore/src/generated/main/native/include/wpi/nt/IntegerTopic.hpp @@ -13,8 +13,6 @@ #include #include -#include "wpi/util/json_fwd.hpp" - #include "wpi/nt/NetworkTableType.hpp" #include "wpi/nt/Topic.hpp" #include "wpi/nt/ntcore_cpp.hpp" @@ -22,6 +20,7 @@ namespace wpi::util { template class SmallVectorImpl; +class json; } // namespace wpi namespace wpi::nt { diff --git a/ntcore/src/generated/main/native/include/wpi/nt/RawTopic.hpp b/ntcore/src/generated/main/native/include/wpi/nt/RawTopic.hpp index 9df0ef01e2..171a6e8369 100644 --- a/ntcore/src/generated/main/native/include/wpi/nt/RawTopic.hpp +++ b/ntcore/src/generated/main/native/include/wpi/nt/RawTopic.hpp @@ -13,8 +13,6 @@ #include #include -#include "wpi/util/json_fwd.hpp" - #include "wpi/nt/NetworkTableType.hpp" #include "wpi/nt/Topic.hpp" #include "wpi/nt/ntcore_cpp.hpp" @@ -22,6 +20,7 @@ namespace wpi::util { template class SmallVectorImpl; +class json; } // namespace wpi namespace wpi::nt { diff --git a/ntcore/src/generated/main/native/include/wpi/nt/StringArrayTopic.hpp b/ntcore/src/generated/main/native/include/wpi/nt/StringArrayTopic.hpp index 4505df388e..2055a96145 100644 --- a/ntcore/src/generated/main/native/include/wpi/nt/StringArrayTopic.hpp +++ b/ntcore/src/generated/main/native/include/wpi/nt/StringArrayTopic.hpp @@ -13,8 +13,6 @@ #include #include -#include "wpi/util/json_fwd.hpp" - #include "wpi/nt/NetworkTableType.hpp" #include "wpi/nt/Topic.hpp" #include "wpi/nt/ntcore_cpp.hpp" @@ -22,6 +20,7 @@ namespace wpi::util { template class SmallVectorImpl; +class json; } // namespace wpi namespace wpi::nt { diff --git a/ntcore/src/generated/main/native/include/wpi/nt/StringTopic.hpp b/ntcore/src/generated/main/native/include/wpi/nt/StringTopic.hpp index 7162c85bfc..e8415154ac 100644 --- a/ntcore/src/generated/main/native/include/wpi/nt/StringTopic.hpp +++ b/ntcore/src/generated/main/native/include/wpi/nt/StringTopic.hpp @@ -15,8 +15,6 @@ #include #include -#include "wpi/util/json_fwd.hpp" - #include "wpi/nt/NetworkTableType.hpp" #include "wpi/nt/Topic.hpp" #include "wpi/nt/ntcore_cpp.hpp" @@ -24,6 +22,7 @@ namespace wpi::util { template class SmallVectorImpl; +class json; } // namespace wpi namespace wpi::nt { diff --git a/ntcore/src/main/native/cpp/ConnectionList.cpp b/ntcore/src/main/native/cpp/ConnectionList.cpp index 20ed596b63..f4c81af1f9 100644 --- a/ntcore/src/main/native/cpp/ConnectionList.cpp +++ b/ntcore/src/main/native/cpp/ConnectionList.cpp @@ -19,18 +19,17 @@ using namespace wpi::nt; static std::string ConnInfoToJson(bool connected, const ConnectionInfo& info) { std::string str; wpi::util::raw_string_ostream os{str}; - wpi::util::json::serializer s{os, ' ', 0}; - os << "{\"connected\":" << (connected ? "true" : "false"); - os << ",\"remote_id\":\""; - s.dump_escaped(info.remote_id, false); - os << "\",\"remote_ip\":\""; - s.dump_escaped(info.remote_ip, false); - os << "\",\"remote_port\":"; - s.dump_integer(static_cast(info.remote_port)); + os << "{\"connected\":"; + wpi::util::json::stringify_bool(os, connected); + os << ",\"remote_id\":"; + wpi::util::json::stringify_string(os, info.remote_id); + os << ",\"remote_ip\":"; + wpi::util::json::stringify_string(os, info.remote_ip); + os << ",\"remote_port\":"; + wpi::util::json::stringify_int(os, info.remote_port); os << ",\"protocol_version\":"; - s.dump_integer(static_cast(info.protocol_version)); + wpi::util::json::stringify_int(os, info.protocol_version); os << "}"; - os.flush(); return str; } diff --git a/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp b/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp index 4819adcb8a..979e1b5227 100644 --- a/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp +++ b/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp @@ -735,7 +735,7 @@ Java_org_wpilib_networktables_NetworkTablesJNI_getTopicProperty (JNIEnv* env, jclass, jint topic, jstring name) { return MakeJString( - env, wpi::nt::GetTopicProperty(topic, JStringRef{env, name}).dump()); + env, wpi::nt::GetTopicProperty(topic, JStringRef{env, name}).to_string()); } /* @@ -747,15 +747,13 @@ JNIEXPORT void JNICALL Java_org_wpilib_networktables_NetworkTablesJNI_setTopicProperty (JNIEnv* env, jclass, jint topic, jstring name, jstring value) { - wpi::util::json j; - try { - j = wpi::util::json::parse(std::string_view{JStringRef{env, value}}); - } catch (wpi::util::json::parse_error& err) { + auto j = wpi::util::json::parse(std::string_view{JStringRef{env, value}}); + if (!j) { illegalArgEx.Throw( - env, fmt::format("could not parse value JSON: {}", err.what())); + env, fmt::format("could not parse value JSON: {}", j.error())); return; } - wpi::nt::SetTopicProperty(topic, JStringRef{env, name}, j); + wpi::nt::SetTopicProperty(topic, JStringRef{env, name}, *j); } /* @@ -779,7 +777,7 @@ JNIEXPORT jstring JNICALL Java_org_wpilib_networktables_NetworkTablesJNI_getTopicProperties (JNIEnv* env, jclass, jint topic) { - return MakeJString(env, wpi::nt::GetTopicProperties(topic).dump()); + return MakeJString(env, wpi::nt::GetTopicProperties(topic).to_string()); } /* @@ -791,19 +789,18 @@ JNIEXPORT void JNICALL Java_org_wpilib_networktables_NetworkTablesJNI_setTopicProperties (JNIEnv* env, jclass, jint topic, jstring properties) { - wpi::util::json j; - try { - j = wpi::util::json::parse(std::string_view{JStringRef{env, properties}}); - } catch (wpi::util::json::parse_error& err) { + auto j = + wpi::util::json::parse(std::string_view{JStringRef{env, properties}}); + if (!j) { illegalArgEx.Throw( - env, fmt::format("could not parse properties JSON: {}", err.what())); + env, fmt::format("could not parse properties JSON: {}", j.error())); return; } - if (!j.is_object()) { + if (!j->is_object()) { illegalArgEx.Throw(env, "properties is not a JSON object"); return; } - wpi::nt::SetTopicProperties(topic, j); + wpi::nt::SetTopicProperties(topic, *j); } /* @@ -856,20 +853,19 @@ Java_org_wpilib_networktables_NetworkTablesJNI_publishEx (JNIEnv* env, jclass, jint topic, jint type, jstring typeStr, jstring properties, jobject options) { - wpi::util::json j; - try { - j = wpi::util::json::parse(std::string_view{JStringRef{env, properties}}); - } catch (wpi::util::json::parse_error& err) { + auto j = + wpi::util::json::parse(std::string_view{JStringRef{env, properties}}); + if (!j) { illegalArgEx.Throw( - env, fmt::format("could not parse properties JSON: {}", err.what())); + env, fmt::format("could not parse properties JSON: {}", j.error())); return 0; } - if (!j.is_object()) { + if (!j->is_object()) { illegalArgEx.Throw(env, "properties is not a JSON object"); return 0; } return wpi::nt::PublishEx(topic, static_cast(type), - JStringRef{env, typeStr}, j, + JStringRef{env, typeStr}, *j, FromJavaPubSubOptions(env, options)); } diff --git a/ntcore/src/main/native/cpp/local/LocalStorageImpl.cpp b/ntcore/src/main/native/cpp/local/LocalStorageImpl.cpp index 5296710f3c..34426efe13 100644 --- a/ntcore/src/main/native/cpp/local/LocalStorageImpl.cpp +++ b/ntcore/src/main/native/cpp/local/LocalStorageImpl.cpp @@ -40,7 +40,7 @@ void StorageImpl::NetworkAnnounce(LocalTopic* topic, std::string_view typeStr, const wpi::util::json& properties, std::optional pubuid) { DEBUG4("LS NetworkAnnounce({}, {}, {}, {})", topic->name, typeStr, - properties.dump(), pubuid.value_or(-1)); + properties.to_string(), pubuid.value_or(-1)); if (pubuid.has_value()) { return; // ack of our publish; ignore } @@ -751,9 +751,10 @@ void StorageImpl::AddSchema(std::string_view name, std::string_view type, return; } - pubHandle = AddLocalPublisher(topic, {{"retained", true}}, - PubSubConfig{NT_RAW, type, {}}) - ->handle; + pubHandle = + AddLocalPublisher(topic, wpi::util::json::object("retained", true), + PubSubConfig{NT_RAW, type, {}}) + ->handle; SetDefaultEntryValue(pubHandle, Value::MakeRaw(schema)); } @@ -870,8 +871,8 @@ void StorageImpl::PropertiesUpdated(LocalTopic* topic, const wpi::util::json& update, unsigned int eventFlags, bool sendNetwork, bool updateFlags) { - DEBUG4("PropertiesUpdated({}, {}, {}, {}, {})", topic->name, update.dump(), - eventFlags, sendNetwork, updateFlags); + DEBUG4("PropertiesUpdated({}, {}, {}, {}, {})", topic->name, + update.to_string(), eventFlags, sendNetwork, updateFlags); topic->RefreshProperties(updateFlags); unsigned int flags = topic->GetFlags(); if (updateFlags && (flags & NT_UNCACHED) != 0 && diff --git a/ntcore/src/main/native/cpp/local/LocalTopic.cpp b/ntcore/src/main/native/cpp/local/LocalTopic.cpp index b201a4b90c..56b8ba8c27 100644 --- a/ntcore/src/main/native/cpp/local/LocalTopic.cpp +++ b/ntcore/src/main/native/cpp/local/LocalTopic.cpp @@ -137,11 +137,11 @@ bool LocalTopic::SetProperties(const wpi::util::json& update) { if (!update.is_object()) { return false; } - for (auto&& change : update.items()) { - if (change.value().is_null()) { - properties.erase(change.key()); + for (auto&& [key, value] : update.get_object()) { + if (value.is_null()) { + properties.erase(key); } else { - properties[change.key()] = change.value(); + properties[key] = value; } } return true; @@ -151,22 +151,25 @@ void LocalTopic::RefreshProperties(bool updateFlags) { if (updateFlags) { RefreshFlags(); } - m_propertiesStr = properties.dump(); + m_propertiesStr = properties.to_string(); } -wpi::util::json LocalTopic::CompareProperties(const wpi::util::json props) { +wpi::util::json LocalTopic::CompareProperties(const wpi::util::json& props) { wpi::util::json update = wpi::util::json::object(); // added/changed - for (auto&& prop : props.items()) { - auto it = properties.find(prop.key()); - if (it == properties.end() || *it != prop.value()) { - update[prop.key()] = prop.value(); + if (!props.is_object()) { + return update; + } + for (auto&& [key, value] : props.get_object()) { + auto it = properties.lookup(key); + if (!it || *it != value) { + update[key] = value; } } // removed - for (auto&& prop : properties.items()) { - if (props.find(prop.key()) == props.end()) { - update[prop.key()] = wpi::util::json(); + for (auto&& [key, value] : properties.get_object()) { + if (!props.contains(key)) { + update[key] = wpi::util::json(); } } return update; @@ -187,30 +190,27 @@ void LocalTopic::ResetIfDoesNotExist() { } void LocalTopic::RefreshFlags() { - auto it = properties.find("persistent"); - if (it != properties.end()) { - if (auto val = it->get_ptr()) { - if (*val) { + if (auto persistent = properties.lookup("persistent")) { + if (persistent->is_bool()) { + if (persistent->get_bool()) { m_flags |= NT_PERSISTENT; } else { m_flags &= ~NT_PERSISTENT; } } } - it = properties.find("retained"); - if (it != properties.end()) { - if (auto val = it->get_ptr()) { - if (*val) { + if (auto retained = properties.lookup("retained")) { + if (retained->is_bool()) { + if (retained->get_bool()) { m_flags |= NT_RETAINED; } else { m_flags &= ~NT_RETAINED; } } } - it = properties.find("cached"); - if (it != properties.end()) { - if (auto val = it->get_ptr()) { - if (*val) { + if (auto cached = properties.lookup("cached")) { + if (cached->is_bool()) { + if (cached->get_bool()) { m_flags &= ~NT_UNCACHED; } else { m_flags |= NT_UNCACHED; diff --git a/ntcore/src/main/native/cpp/local/LocalTopic.hpp b/ntcore/src/main/native/cpp/local/LocalTopic.hpp index 2bb67e6a5a..913218b1fd 100644 --- a/ntcore/src/main/native/cpp/local/LocalTopic.hpp +++ b/ntcore/src/main/native/cpp/local/LocalTopic.hpp @@ -62,7 +62,7 @@ struct LocalTopic { void RefreshProperties(bool updateFlags); // returns update json - wpi::util::json CompareProperties(const wpi::util::json props); + wpi::util::json CompareProperties(const wpi::util::json& props); TopicInfo GetTopicInfo() const { TopicInfo info; diff --git a/ntcore/src/main/native/cpp/net/ClientImpl.cpp b/ntcore/src/main/native/cpp/net/ClientImpl.cpp index 3f2aae1291..724c0672b6 100644 --- a/ntcore/src/main/native/cpp/net/ClientImpl.cpp +++ b/ntcore/src/main/native/cpp/net/ClientImpl.cpp @@ -240,7 +240,7 @@ void ClientImpl::ServerUnannounce(std::string_view name, int id) { void ClientImpl::ServerPropertiesUpdate(std::string_view name, const wpi::util::json& update, bool ack) { - DEBUG4("ServerProperties({}, {}, {})", name, update.dump(), ack); + DEBUG4("ServerProperties({}, {}, {})", name, update.to_string(), ack); assert(m_local); m_local->ServerPropertiesUpdate(name, update, ack); } diff --git a/ntcore/src/main/native/cpp/net/MessageHandler.hpp b/ntcore/src/main/native/cpp/net/MessageHandler.hpp index daba1060d5..7381d24da3 100644 --- a/ntcore/src/main/native/cpp/net/MessageHandler.hpp +++ b/ntcore/src/main/native/cpp/net/MessageHandler.hpp @@ -9,7 +9,9 @@ #include #include -#include "wpi/util/json_fwd.hpp" +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpi::nt { class PubSubOptionsImpl; diff --git a/ntcore/src/main/native/cpp/net/NetworkOutgoingQueue.hpp b/ntcore/src/main/native/cpp/net/NetworkOutgoingQueue.hpp index e9250369ab..75d5390618 100644 --- a/ntcore/src/main/native/cpp/net/NetworkOutgoingQueue.hpp +++ b/ntcore/src/main/native/cpp/net/NetworkOutgoingQueue.hpp @@ -20,6 +20,8 @@ #include "wpi/nt/NetworkTableValue.hpp" #include "wpi/nt/ntcore_c.h" #include "wpi/util/DenseMap.hpp" +#include "wpi/util/SmallVector.hpp" +#include "wpi/util/raw_ostream.hpp" namespace wpi::nt::net { diff --git a/ntcore/src/main/native/cpp/net/WireDecoder.cpp b/ntcore/src/main/native/cpp/net/WireDecoder.cpp index c588752213..861ac91872 100644 --- a/ntcore/src/main/native/cpp/net/WireDecoder.cpp +++ b/ntcore/src/main/native/cpp/net/WireDecoder.cpp @@ -24,12 +24,8 @@ using namespace wpi::nt::net; using namespace mpack; static bool GetNumber(wpi::util::json& val, double* num) { - if (auto v = val.get_ptr()) { - *num = *v; - } else if (auto v = val.get_ptr()) { - *num = *v; - } else if (auto v = val.get_ptr()) { - *num = *v; + if (val.is_number()) { + *num = val.get_number(); } else { return false; } @@ -37,67 +33,64 @@ static bool GetNumber(wpi::util::json& val, double* num) { } static bool GetNumber(wpi::util::json& val, int64_t* num) { - if (auto v = val.get_ptr()) { - *num = *v; - } else if (auto v = val.get_ptr()) { - *num = *v; + if (val.is_int()) { + *num = val.get_int(); } else { return false; } return true; } -static std::string* ObjGetString(wpi::util::json::object_t& obj, - std::string_view key, std::string* error) { - auto it = obj.find(key); - if (it == obj.end()) { +static std::string* ObjGetString(wpi::util::json& obj, std::string_view key, + std::string* error) { + auto val = obj.lookup(key); + if (!val) { *error = fmt::format("no {} key", key); return nullptr; } - auto val = it->second.get_ptr(); - if (!val) { + if (!val->is_string()) { *error = fmt::format("{} must be a string", key); + return nullptr; } - return val; + return &val->get_string(); } -static bool ObjGetNumber(wpi::util::json::object_t& obj, std::string_view key, +static bool ObjGetNumber(wpi::util::json& obj, std::string_view key, std::string* error, int64_t* num) { - auto it = obj.find(key); - if (it == obj.end()) { + auto val = obj.lookup(key); + if (!val) { *error = fmt::format("no {} key", key); return false; } - if (!GetNumber(it->second, num)) { + if (!GetNumber(*val, num)) { *error = fmt::format("{} must be a number", key); return false; } return true; } -static bool ObjGetStringArray(wpi::util::json::object_t& obj, - std::string_view key, std::string* error, +static bool ObjGetStringArray(wpi::util::json& obj, std::string_view key, + std::string* error, std::vector* out) { // prefixes - auto it = obj.find(key); - if (it == obj.end()) { + auto val = obj.lookup(key); + if (!val) { *error = fmt::format("no {} key", key); return false; } - auto jarr = it->second.get_ptr(); - if (!jarr) { + if (!val->is_array()) { *error = fmt::format("{} must be an array", key); return false; } + auto& arr = val->get_array(); out->resize(0); - out->reserve(jarr->size()); - for (auto&& jval : *jarr) { - auto str = jval.get_ptr(); - if (!str) { + out->reserve(arr.size()); + for (auto&& jval : arr) { + if (!jval.is_string()) { *error = fmt::format("{}/{} must be a string", key, out->size()); return false; } - out->emplace_back(std::move(*str)); + out->emplace_back(jval.get_string()); } return true; } @@ -113,43 +106,39 @@ template std::same_as) static bool WireDecodeTextImpl(std::string_view in, T& out, wpi::util::Logger& logger) { - wpi::util::json j; - try { - j = wpi::util::json::parse(in); - } catch (wpi::util::json::parse_error& err) { - WPI_WARNING(logger, "could not decode JSON message: {}", err.what()); + auto j = wpi::util::json::parse(in); + if (!j) { + WPI_WARNING(logger, "could not decode JSON message: {}", j.error()); return false; } - if (!j.is_array()) { + if (!j->is_array()) { WPI_WARNING(logger, "expected JSON array at top level"); return false; } bool rv = false; int i = -1; - for (auto&& jmsg : j) { + for (auto&& jmsg : j->get_array()) { ++i; std::string error; { - auto obj = jmsg.get_ptr(); - if (!obj) { + if (!jmsg.is_object()) { error = "expected message to be an object"; goto err; } - auto method = ObjGetString(*obj, "method", &error); + auto method = ObjGetString(jmsg, "method", &error); if (!method) { goto err; } - auto paramsIt = obj->find("params"); - if (paramsIt == obj->end()) { + auto params = jmsg.lookup("params"); + if (!params) { error = "no params key"; goto err; } - auto params = paramsIt->second.get_ptr(); - if (!params) { + if (!params->is_object()) { error = "params must be an object"; goto err; } @@ -182,14 +171,10 @@ static bool WireDecodeTextImpl(std::string_view in, T& out, } // properties; allow missing (treated as empty) - wpi::util::json* properties = nullptr; - auto propertiesIt = params->find("properties"); - if (propertiesIt != params->end()) { - properties = &propertiesIt->second; - if (!properties->is_object()) { - error = "properties must be an object"; - goto err; - } + auto properties = params->lookup("properties"); + if (properties && !properties->is_object()) { + error = "properties must be an object"; + goto err; } wpi::util::json propertiesEmpty; if (!properties) { @@ -225,12 +210,11 @@ static bool WireDecodeTextImpl(std::string_view in, T& out, } // update - auto updateIt = params->find("update"); - if (updateIt == params->end()) { + auto update = params->lookup("update"); + if (!update) { error = "no update key"; goto err; } - auto update = &updateIt->second; if (!update->is_object()) { error = "update must be an object"; goto err; @@ -254,20 +238,16 @@ static bool WireDecodeTextImpl(std::string_view in, T& out, // options PubSubOptionsImpl options; - auto optionsIt = params->find("options"); - if (optionsIt != params->end()) { - auto joptions = - optionsIt->second.get_ptr(); - if (!joptions) { + if (auto joptions = params->lookup("options")) { + if (!joptions->is_object()) { error = "options must be an object"; goto err; } // periodic - auto periodicIt = joptions->find("periodic"); - if (periodicIt != joptions->end()) { + if (auto periodic = joptions->lookup("periodic")) { double val; - if (!GetNumber(periodicIt->second, &val)) { + if (!GetNumber(*periodic, &val)) { error = "periodic value must be a number"; goto err; } @@ -276,36 +256,30 @@ static bool WireDecodeTextImpl(std::string_view in, T& out, } // send all changes - auto sendAllIt = joptions->find("all"); - if (sendAllIt != joptions->end()) { - auto sendAll = sendAllIt->second.get_ptr(); - if (!sendAll) { + if (auto sendAll = joptions->lookup("all")) { + if (!sendAll->is_bool()) { error = "all value must be a boolean"; goto err; } - options.sendAll = *sendAll; + options.sendAll = sendAll->get_bool(); } // topics only - auto topicsOnlyIt = joptions->find("topicsonly"); - if (topicsOnlyIt != joptions->end()) { - auto topicsOnly = topicsOnlyIt->second.get_ptr(); - if (!topicsOnly) { + if (auto topicsOnly = joptions->lookup("topicsonly")) { + if (!topicsOnly->is_bool()) { error = "topicsonly value must be a boolean"; goto err; } - options.topicsOnly = *topicsOnly; + options.topicsOnly = topicsOnly->get_bool(); } // prefix match - auto prefixMatchIt = joptions->find("prefix"); - if (prefixMatchIt != joptions->end()) { - auto prefixMatch = prefixMatchIt->second.get_ptr(); - if (!prefixMatch) { + if (auto prefixMatch = joptions->lookup("prefix")) { + if (!prefixMatch->is_bool()) { error = "prefix value must be a boolean"; goto err; } - options.prefixMatch = *prefixMatch; + options.prefixMatch = prefixMatch->get_bool(); } } @@ -368,10 +342,9 @@ static bool WireDecodeTextImpl(std::string_view in, T& out, // pubuid std::optional pubuid; - auto pubuidIt = params->find("pubuid"); - if (pubuidIt != params->end()) { + if (auto jpubuid = params->lookup("pubuid")) { int64_t val; - if (!GetNumber(pubuidIt->second, &val)) { + if (!GetNumber(*jpubuid, &val)) { error = "pubuid value must be a number"; goto err; } @@ -387,12 +360,11 @@ static bool WireDecodeTextImpl(std::string_view in, T& out, } // properties - auto propertiesIt = params->find("properties"); - if (propertiesIt == params->end()) { + auto properties = params->lookup("properties"); + if (!properties) { error = "no properties key"; goto err; } - auto properties = &propertiesIt->second; if (!properties->is_object()) { WPI_WARNING(logger, "{}: properties is not an object", *name); *properties = wpi::util::json::object(); @@ -430,26 +402,23 @@ static bool WireDecodeTextImpl(std::string_view in, T& out, } // update - auto updateIt = params->find("update"); - if (updateIt == params->end()) { + auto update = params->lookup("update"); + if (!update) { error = "no update key"; goto err; } - auto update = &updateIt->second; if (!update->is_object()) { error = "update must be an object"; goto err; } bool ack = false; - auto ackIt = params->find("ack"); - if (ackIt != params->end()) { - auto val = ackIt->second.get_ptr(); - if (!val) { + if (auto jack = params->lookup("ack")) { + if (!jack->is_bool()) { error = "ack must be a boolean"; goto err; } - ack = *val; + ack = jack->get_bool(); } // complete diff --git a/ntcore/src/main/native/cpp/net/WireEncoder.cpp b/ntcore/src/main/native/cpp/net/WireEncoder.cpp index ee0529354b..ddb4860cc3 100644 --- a/ntcore/src/main/native/cpp/net/WireEncoder.cpp +++ b/ntcore/src/main/native/cpp/net/WireEncoder.cpp @@ -22,43 +22,39 @@ void wpi::nt::net::WireEncodePublish(wpi::util::raw_ostream& os, int pubuid, std::string_view name, std::string_view typeStr, const wpi::util::json& properties) { - wpi::util::json::serializer s{os, ' ', 0}; os << "{\"method\":\"" << PublishMsg::kMethodStr << "\",\"params\":{"; - os << "\"name\":\""; - s.dump_escaped(name, false); - os << "\",\"properties\":"; - s.dump(properties, false, false, 0, 0); + os << "\"name\":"; + wpi::util::json::stringify_string(os, name); + os << ",\"properties\":"; + properties.marshal(os); os << ",\"pubuid\":"; - s.dump_integer(pubuid); - os << ",\"type\":\""; - s.dump_escaped(typeStr, false); - os << "\"}}"; + wpi::util::json::stringify_int(os, pubuid); + os << ",\"type\":"; + wpi::util::json::stringify_string(os, typeStr); + os << "}}"; } void wpi::nt::net::WireEncodeUnpublish(wpi::util::raw_ostream& os, int pubuid) { - wpi::util::json::serializer s{os, ' ', 0}; os << "{\"method\":\"" << UnpublishMsg::kMethodStr << "\",\"params\":{"; os << "\"pubuid\":"; - s.dump_integer(pubuid); + wpi::util::json::stringify_int(os, pubuid); os << "}}"; } void wpi::nt::net::WireEncodeSetProperties(wpi::util::raw_ostream& os, std::string_view name, const wpi::util::json& update) { - wpi::util::json::serializer s{os, ' ', 0}; os << "{\"method\":\"" << SetPropertiesMsg::kMethodStr << "\",\"params\":{"; - os << "\"name\":\""; - s.dump_escaped(name, false); - os << "\",\"update\":"; - s.dump(update, false, false, 0, 0); + os << "\"name\":"; + wpi::util::json::stringify_string(os, name); + os << ",\"update\":"; + update.marshal(os); os << "}}"; } template static void EncodePrefixes(wpi::util::raw_ostream& os, - std::span topicNames, - wpi::util::json::serializer& s) { + std::span topicNames) { os << '['; bool first = true; for (auto&& name : topicNames) { @@ -67,9 +63,7 @@ static void EncodePrefixes(wpi::util::raw_ostream& os, } else { os << ','; } - os << '"'; - s.dump_escaped(name, false); - os << '"'; + wpi::util::json::stringify_string(os, name); } os << ']'; } @@ -78,7 +72,6 @@ template static void WireEncodeSubscribeImpl(wpi::util::raw_ostream& os, int subuid, std::span topicNames, const PubSubOptionsImpl& options) { - wpi::util::json::serializer s{os, ' ', 0}; os << "{\"method\":\"" << SubscribeMsg::kMethodStr << "\",\"params\":{"; os << "\"options\":{"; bool first = true; @@ -105,12 +98,12 @@ static void WireEncodeSubscribeImpl(wpi::util::raw_ostream& os, int subuid, os << ','; } os << "\"periodic\":"; - s.dump_float(options.periodicMs / 1000.0); + wpi::util::json::stringify_float(os, options.periodicMs / 1000.0); } os << "},\"topics\":"; - EncodePrefixes(os, topicNames, s); + EncodePrefixes(os, topicNames); os << ",\"subuid\":"; - s.dump_integer(subuid); + wpi::util::json::stringify_int(os, subuid); os << "}}"; } @@ -129,10 +122,9 @@ void wpi::nt::net::WireEncodeSubscribe(wpi::util::raw_ostream& os, int subuid, void wpi::nt::net::WireEncodeUnsubscribe(wpi::util::raw_ostream& os, int subuid) { - wpi::util::json::serializer s{os, ' ', 0}; os << "{\"method\":\"" << UnsubscribeMsg::kMethodStr << "\",\"params\":{"; os << "\"subuid\":"; - s.dump_integer(subuid); + wpi::util::json::stringify_int(os, subuid); os << "}}"; } @@ -159,45 +151,42 @@ void wpi::nt::net::WireEncodeAnnounce(wpi::util::raw_ostream& os, std::string_view typeStr, const wpi::util::json& properties, std::optional pubuid) { - wpi::util::json::serializer s{os, ' ', 0}; os << "{\"method\":\"" << AnnounceMsg::kMethodStr << "\",\"params\":{"; os << "\"id\":"; - s.dump_integer(id); - os << ",\"name\":\""; - s.dump_escaped(name, false); - os << "\",\"properties\":"; - s.dump(properties, false, false, 0, 0); + wpi::util::json::stringify_int(os, id); + os << ",\"name\":"; + wpi::util::json::stringify_string(os, name); + os << ",\"properties\":"; + properties.marshal(os); if (pubuid) { os << ",\"pubuid\":"; - s.dump_integer(*pubuid); + wpi::util::json::stringify_int(os, *pubuid); } - os << ",\"type\":\""; - s.dump_escaped(typeStr, false); - os << "\"}}"; + os << ",\"type\":"; + wpi::util::json::stringify_string(os, typeStr); + os << "}}"; } void wpi::nt::net::WireEncodeUnannounce(wpi::util::raw_ostream& os, std::string_view name, int64_t id) { - wpi::util::json::serializer s{os, ' ', 0}; os << "{\"method\":\"" << UnannounceMsg::kMethodStr << "\",\"params\":{"; os << "\"id\":"; - s.dump_integer(id); - os << ",\"name\":\""; - s.dump_escaped(name, false); - os << "\"}}"; + wpi::util::json::stringify_int(os, id); + os << ",\"name\":"; + wpi::util::json::stringify_string(os, name); + os << "}}"; } void wpi::nt::net::WireEncodePropertiesUpdate(wpi::util::raw_ostream& os, std::string_view name, const wpi::util::json& update, bool ack) { - wpi::util::json::serializer s{os, ' ', 0}; os << "{\"method\":\"" << PropertiesUpdateMsg::kMethodStr << "\",\"params\":{"; - os << "\"name\":\""; - s.dump_escaped(name, false); - os << "\",\"update\":"; - s.dump(update, false, false, 0, 0); + os << "\"name\":"; + wpi::util::json::stringify_string(os, name); + os << ",\"update\":"; + update.marshal(os); if (ack) { os << ",\"ack\":true"; } diff --git a/ntcore/src/main/native/cpp/net/WireEncoder.hpp b/ntcore/src/main/native/cpp/net/WireEncoder.hpp index a016b876ea..ee357f9530 100644 --- a/ntcore/src/main/native/cpp/net/WireEncoder.hpp +++ b/ntcore/src/main/native/cpp/net/WireEncoder.hpp @@ -9,9 +9,8 @@ #include #include -#include "wpi/util/json_fwd.hpp" - namespace wpi::util { +class json; class raw_ostream; } // namespace wpi::util diff --git a/ntcore/src/main/native/cpp/ntcore_c.cpp b/ntcore/src/main/native/cpp/ntcore_c.cpp index 2550708c0a..efdf555473 100644 --- a/ntcore/src/main/native/cpp/ntcore_c.cpp +++ b/ntcore/src/main/native/cpp/ntcore_c.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "Value_internal.hpp" @@ -313,18 +314,16 @@ void NT_GetTopicProperty(NT_Topic topic, const struct WPI_String* name, struct WPI_String* prop) { wpi::util::json j = wpi::nt::GetTopicProperty(topic, wpi::util::to_string_view(name)); - wpi::nt::ConvertToC(j.dump(), prop); + wpi::nt::ConvertToC(j.to_string(), prop); } NT_Bool NT_SetTopicProperty(NT_Topic topic, const struct WPI_String* name, const struct WPI_String* value) { - wpi::util::json j; - try { - j = wpi::util::json::parse(wpi::util::to_string_view(value)); - } catch (wpi::util::json::parse_error&) { + auto j = wpi::util::json::parse(wpi::util::to_string_view(value)); + if (!j) { return false; } - wpi::nt::SetTopicProperty(topic, wpi::util::to_string_view(name), j); + wpi::nt::SetTopicProperty(topic, wpi::util::to_string_view(name), *j); return true; } @@ -334,18 +333,16 @@ void NT_DeleteTopicProperty(NT_Topic topic, const struct WPI_String* name) { void NT_GetTopicProperties(NT_Topic topic, struct WPI_String* property) { wpi::util::json j = wpi::nt::GetTopicProperties(topic); - wpi::nt::ConvertToC(j.dump(), property); + wpi::nt::ConvertToC(j.to_string(), property); } NT_Bool NT_SetTopicProperties(NT_Topic topic, const struct WPI_String* properties) { - wpi::util::json j; - try { - j = wpi::util::json::parse(wpi::util::to_string_view(properties)); - } catch (wpi::util::json::parse_error&) { + auto j = wpi::util::json::parse(wpi::util::to_string_view(properties)); + if (!j) { return false; } - return wpi::nt::SetTopicProperties(topic, j); + return wpi::nt::SetTopicProperties(topic, *j); } NT_Subscriber NT_Subscribe(NT_Topic topic, NT_Type type, @@ -375,11 +372,11 @@ NT_Publisher NT_PublishEx(NT_Topic topic, NT_Type type, // gracefully handle empty string j = wpi::util::json::object(); } else { - try { - j = wpi::util::json::parse(wpi::util::to_string_view(properties)); - } catch (wpi::util::json::parse_error&) { + auto ex = wpi::util::json::parse(wpi::util::to_string_view(properties)); + if (!ex) { return {}; } + j = std::move(*ex); } return wpi::nt::PublishEx(topic, type, wpi::util::to_string_view(typeStr), j, diff --git a/ntcore/src/main/native/cpp/ntcore_cpp.cpp b/ntcore/src/main/native/cpp/ntcore_cpp.cpp index fa5be9558a..c2467260c3 100644 --- a/ntcore/src/main/native/cpp/ntcore_cpp.cpp +++ b/ntcore/src/main/native/cpp/ntcore_cpp.cpp @@ -29,11 +29,7 @@ static std::atomic gNowTime; namespace wpi::nt { wpi::util::json TopicInfo::GetProperties() const { - try { - return wpi::util::json::parse(properties); - } catch (wpi::util::json::parse_error&) { - return wpi::util::json::object(); - } + return wpi::util::json::parse(properties).value_or(wpi::util::json::object()); } /* diff --git a/ntcore/src/main/native/cpp/server/ServerClient.hpp b/ntcore/src/main/native/cpp/server/ServerClient.hpp index 59ec3fcba8..c4c90272a7 100644 --- a/ntcore/src/main/native/cpp/server/ServerClient.hpp +++ b/ntcore/src/main/native/cpp/server/ServerClient.hpp @@ -17,9 +17,9 @@ #include "server/Functions.hpp" #include "server/ServerPublisher.hpp" #include "server/ServerSubscriber.hpp" -#include "wpi/util/json_fwd.hpp" namespace wpi::util { +class json; class Logger; template class SmallVectorImpl; diff --git a/ntcore/src/main/native/cpp/server/ServerClient4Base.cpp b/ntcore/src/main/native/cpp/server/ServerClient4Base.cpp index e768d068ca..e1a090a978 100644 --- a/ntcore/src/main/native/cpp/server/ServerClient4Base.cpp +++ b/ntcore/src/main/native/cpp/server/ServerClient4Base.cpp @@ -4,6 +4,7 @@ #include "ServerClient4Base.hpp" +#include #include #include #include @@ -38,7 +39,8 @@ void ServerClient4Base::ClientPublish(int pubuid, std::string_view name, } // respond with announce with pubuid to client - DEBUG4("client {}: announce {} pubuid {}", m_id, topic->name, pubuid); + DEBUG4("client {}: announce {} pubuid {} properties {}", m_id, topic->name, + pubuid, properties.to_string()); SendAnnounce(topic, pubuid); } @@ -68,18 +70,18 @@ void ServerClient4Base::ClientUnpublish(int pubuid) { void ServerClient4Base::ClientSetProperties(std::string_view name, const wpi::util::json& update) { - DEBUG4("ClientSetProperties({}, {}, {})", m_id, name, update.dump()); + DEBUG4("ClientSetProperties({}, {}, {})", m_id, name, update.to_string()); ServerTopic* topic = m_storage.GetTopic(name); if (!topic || !topic->IsPublished()) { WARN( "server ignoring SetProperties({}) from client {} on unpublished topic " "'{}'; publish or set a value first", - update.dump(), m_id, name); + update.to_string(), m_id, name); return; // nothing to do } if (topic->special) { WARN("server ignoring SetProperties({}) from client {} on meta topic '{}'", - update.dump(), m_id, name); + update.to_string(), m_id, name); return; // nothing to do } m_storage.SetProperties(nullptr, topic, update); diff --git a/ntcore/src/main/native/cpp/server/ServerClient4Base.hpp b/ntcore/src/main/native/cpp/server/ServerClient4Base.hpp index 1f035de321..872b7495d8 100644 --- a/ntcore/src/main/native/cpp/server/ServerClient4Base.hpp +++ b/ntcore/src/main/native/cpp/server/ServerClient4Base.hpp @@ -4,6 +4,7 @@ #pragma once +#include #include #include diff --git a/ntcore/src/main/native/cpp/server/ServerImpl.cpp b/ntcore/src/main/native/cpp/server/ServerImpl.cpp index 4ce79752be..186bd70893 100644 --- a/ntcore/src/main/native/cpp/server/ServerImpl.cpp +++ b/ntcore/src/main/native/cpp/server/ServerImpl.cpp @@ -111,7 +111,8 @@ void ServerImpl::SendAnnounce(ServerTopic* topic, ServerClient* client) { continue; // don't announce to requesting client again } - DEBUG4("client {}: announce {}", aClient->GetId(), topic->name); + DEBUG4("client {}: announce {} properties {}", aClient->GetId(), + topic->name, topic->properties.to_string()); aClient->SendAnnounce(topic, std::nullopt); } } diff --git a/ntcore/src/main/native/cpp/server/ServerStorage.cpp b/ntcore/src/main/native/cpp/server/ServerStorage.cpp index c039fcdd32..03fbce5810 100644 --- a/ntcore/src/main/native/cpp/server/ServerStorage.cpp +++ b/ntcore/src/main/native/cpp/server/ServerStorage.cpp @@ -36,6 +36,8 @@ ServerTopic* ServerStorage::CreateTopic(ServerClient* client, } } } else { + DEBUG4("creating topic '{}' with type '{}' and properties {}", name, + typeStr, properties.to_string()); // new topic unsigned int id = m_topics.emplace_back( std::make_unique(m_logger, name, typeStr, properties)); @@ -58,7 +60,8 @@ ServerTopic* ServerStorage::CreateTopic(ServerClient* client, } ServerTopic* ServerStorage::CreateMetaTopic(std::string_view name) { - return CreateTopic(nullptr, name, "msgpack", {{"retained", true}}, true); + return CreateTopic(nullptr, name, "msgpack", + wpi::util::json::object("retained", true), true); } void ServerStorage::DeleteTopic(ServerTopic* topic) { @@ -90,7 +93,7 @@ void ServerStorage::DeleteTopic(ServerTopic* topic) { void ServerStorage::SetProperties(ServerClient* client, ServerTopic* topic, const wpi::util::json& update) { DEBUG4("SetProperties({}, {}, {})", client ? client->GetId() : -1, - topic->name, update.dump()); + topic->name, update.to_string()); bool wasPersistent = topic->persistent; if (topic->SetProperties(update)) { // update persistentChanged flag @@ -110,9 +113,10 @@ void ServerStorage::SetFlags(ServerClient* client, ServerTopic* topic, m_persistentChanged = true; wpi::util::json update; if (topic->persistent) { - update = {{"persistent", true}}; + update = wpi::util::json::object("persistent", true); } else { - update = {{"persistent", wpi::util::json::object()}}; + update = + wpi::util::json::object("persistent", wpi::util::json::object()); } PropertiesChanged(client, topic, update); } @@ -234,29 +238,22 @@ void ServerStorage::PropertiesChanged(ServerClient* client, ServerTopic* topic, } } -static void DumpValue(wpi::util::raw_ostream& os, const Value& value, - wpi::util::json::serializer& s) { +static void DumpValue(wpi::util::raw_ostream& os, const Value& value) { switch (value.type()) { case NT_BOOLEAN: - if (value.GetBoolean()) { - os << "true"; - } else { - os << "false"; - } + wpi::util::json::stringify_bool(os, value.GetBoolean()); break; case NT_DOUBLE: - s.dump_float(value.GetDouble()); + wpi::util::json::stringify_double(os, value.GetDouble()); break; case NT_FLOAT: - s.dump_float(value.GetFloat()); + wpi::util::json::stringify_float(os, value.GetFloat()); break; case NT_INTEGER: - s.dump_integer(value.GetInteger()); + wpi::util::json::stringify_int(os, value.GetInteger()); break; case NT_STRING: - os << '"'; - s.dump_escaped(value.GetString(), false); - os << '"'; + wpi::util::json::stringify_string(os, value.GetString()); break; case NT_RAW: case NT_RPC: @@ -273,11 +270,7 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value, } else { os << ", "; } - if (v) { - os << "true"; - } else { - os << "false"; - } + wpi::util::json::stringify_bool(os, v); } os << ']'; break; @@ -291,7 +284,7 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value, } else { os << ", "; } - s.dump_float(v); + wpi::util::json::stringify_double(os, v); } os << ']'; break; @@ -305,7 +298,7 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value, } else { os << ", "; } - s.dump_float(v); + wpi::util::json::stringify_float(os, v); } os << ']'; break; @@ -319,7 +312,7 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value, } else { os << ", "; } - s.dump_integer(v); + wpi::util::json::stringify_int(os, v); } os << ']'; break; @@ -333,9 +326,7 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value, } else { os << ", "; } - os << '"'; - s.dump_escaped(v, false); - os << '"'; + wpi::util::json::stringify_string(os, v); } os << ']'; break; @@ -347,7 +338,6 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value, } void ServerStorage::DumpPersistent(wpi::util::raw_ostream& os) { - wpi::util::json::serializer s{os, ' ', 16}; os << "[\n"; bool first = true; for (const auto& topic : m_topics) { @@ -359,31 +349,31 @@ void ServerStorage::DumpPersistent(wpi::util::raw_ostream& os) { } else { os << ",\n"; } - os << " {\n \"name\": \""; - s.dump_escaped(topic->name, false); - os << "\",\n \"type\": \""; - s.dump_escaped(topic->typeStr, false); - os << "\",\n \"value\": "; - DumpValue(os, topic->lastValue, s); + os << " {\n \"name\": "; + wpi::util::json::stringify_string(os, topic->name); + os << ",\n \"type\": "; + wpi::util::json::stringify_string(os, topic->typeStr); + os << ",\n \"value\": "; + DumpValue(os, topic->lastValue); os << ",\n \"properties\": "; - s.dump(topic->properties, true, false, 2, 4); + topic->properties.marshal(os, true, 2); os << "\n }"; } os << "\n]\n"; } -static std::string* ObjGetString(wpi::util::json::object_t& obj, - std::string_view key, std::string* error) { - auto it = obj.find(key); - if (it == obj.end()) { +static std::string* ObjGetString(wpi::util::json& obj, std::string_view key, + std::string* error) { + auto value = obj.lookup(key); + if (!value) { *error = fmt::format("no {} key", key); return nullptr; } - auto val = it->second.get_ptr(); - if (!val) { + if (!value->is_string()) { *error = fmt::format("{} must be a string", key); + return nullptr; } - return val; + return &value->get_string(); } std::string ServerStorage::LoadPersistent(std::string_view in) { @@ -391,14 +381,12 @@ std::string ServerStorage::LoadPersistent(std::string_view in) { return {}; } - wpi::util::json j; - try { - j = wpi::util::json::parse(in); - } catch (wpi::util::json::parse_error& err) { - return fmt::format("could not decode JSON: {}", err.what()); + auto j = wpi::util::json::parse(in); + if (!j) { + return fmt::format("could not decode JSON: {}", j.error()); } - if (!j.is_array()) { + if (!j->is_array()) { return "expected JSON array at top level"; } @@ -407,48 +395,46 @@ std::string ServerStorage::LoadPersistent(std::string_view in) { std::string allerrors; int i = -1; auto time = wpi::nt::Now(); - for (auto&& jitem : j) { + for (auto&& jitem : j->get_array()) { ++i; std::string error; { - auto obj = jitem.get_ptr(); - if (!obj) { + if (!jitem.is_object()) { error = "expected item to be an object"; goto err; } // name - auto name = ObjGetString(*obj, "name", &error); + auto name = ObjGetString(jitem, "name", &error); if (!name) { goto err; } // type - auto typeStr = ObjGetString(*obj, "type", &error); + auto typeStr = ObjGetString(jitem, "type", &error); if (!typeStr) { goto err; } // properties - auto propsIt = obj->find("properties"); - if (propsIt == obj->end()) { + auto props = jitem.lookup("properties"); + if (!props) { error = "no properties key"; goto err; } - auto& props = propsIt->second; - if (!props.is_object()) { + if (!props->is_object()) { error = "properties must be an object"; goto err; } // check to make sure persistent property is set - auto persistentIt = props.find("persistent"); - if (persistentIt == props.end()) { + auto persistent = props->lookup("persistent"); + if (!persistent) { error = "no persistent property"; goto err; } - if (auto v = persistentIt->get_ptr()) { - if (!*v) { + if (persistent->is_bool()) { + if (!persistent->get_bool()) { error = "persistent property is false"; goto err; } @@ -458,121 +444,112 @@ std::string ServerStorage::LoadPersistent(std::string_view in) { } // value - auto valueIt = obj->find("value"); - if (valueIt == obj->end()) { + auto jvalue = jitem.lookup("value"); + if (!jvalue) { error = "no value key"; goto err; } Value value; if (*typeStr == "boolean") { - if (auto v = valueIt->second.get_ptr()) { - value = Value::MakeBoolean(*v, time); + if (jvalue->is_bool()) { + value = Value::MakeBoolean(jvalue->get_bool(), time); } else { error = "value type mismatch, expected boolean"; goto err; } } else if (*typeStr == "int") { - if (auto v = valueIt->second.get_ptr()) { - value = Value::MakeInteger(*v, time); - } else if (auto v = valueIt->second.get_ptr()) { - value = Value::MakeInteger(*v, time); + if (jvalue->is_int()) { + value = Value::MakeInteger(jvalue->get_int(), time); } else { error = "value type mismatch, expected int"; goto err; } } else if (*typeStr == "float") { - if (auto v = valueIt->second.get_ptr()) { - value = Value::MakeFloat(*v, time); + if (jvalue->is_number()) { + value = Value::MakeFloat(jvalue->get_number(), time); } else { error = "value type mismatch, expected float"; goto err; } } else if (*typeStr == "double") { - if (auto v = valueIt->second.get_ptr()) { - value = Value::MakeDouble(*v, time); + if (jvalue->is_number()) { + value = Value::MakeDouble(jvalue->get_number(), time); } else { error = "value type mismatch, expected double"; goto err; } } else if (*typeStr == "string" || *typeStr == "json") { - if (auto v = valueIt->second.get_ptr()) { - value = Value::MakeString(*v, time); + if (jvalue->is_string()) { + value = Value::MakeString(jvalue->get_string(), time); } else { error = "value type mismatch, expected string"; goto err; } } else if (*typeStr == "boolean[]") { - auto arr = valueIt->second.get_ptr(); - if (!arr) { + if (!jvalue->is_array()) { error = "value type mismatch, expected array"; goto err; } std::vector elems; - for (auto&& jelem : valueIt->second) { - if (auto v = jelem.get_ptr()) { - elems.push_back(*v); + for (auto&& jelem : jvalue->get_array()) { + if (jelem.is_bool()) { + elems.push_back(jelem.get_bool()); } else { error = "value type mismatch, expected boolean"; } } value = Value::MakeBooleanArray(elems, time); } else if (*typeStr == "int[]") { - auto arr = valueIt->second.get_ptr(); - if (!arr) { + if (!jvalue->is_array()) { error = "value type mismatch, expected array"; goto err; } std::vector elems; - for (auto&& jelem : valueIt->second) { - if (auto v = jelem.get_ptr()) { - elems.push_back(*v); - } else if (auto v = jelem.get_ptr()) { - elems.push_back(*v); + for (auto&& jelem : jvalue->get_array()) { + if (jelem.is_int()) { + elems.push_back(jelem.get_int()); } else { error = "value type mismatch, expected int"; } } value = Value::MakeIntegerArray(elems, time); } else if (*typeStr == "double[]") { - auto arr = valueIt->second.get_ptr(); - if (!arr) { + if (!jvalue->is_array()) { error = "value type mismatch, expected array"; goto err; } std::vector elems; - for (auto&& jelem : valueIt->second) { - if (auto v = jelem.get_ptr()) { - elems.push_back(*v); + for (auto&& jelem : jvalue->get_array()) { + if (jelem.is_number()) { + elems.push_back(jelem.get_number()); } else { error = "value type mismatch, expected double"; } } value = Value::MakeDoubleArray(elems, time); } else if (*typeStr == "float[]") { - auto arr = valueIt->second.get_ptr(); - if (!arr) { + if (!jvalue->is_array()) { error = "value type mismatch, expected array"; goto err; } std::vector elems; - for (auto&& jelem : valueIt->second) { - if (auto v = jelem.get_ptr()) { - elems.push_back(*v); + for (auto&& jelem : jvalue->get_array()) { + if (jelem.is_number()) { + elems.push_back(jelem.get_number()); } else { error = "value type mismatch, expected float"; } } value = Value::MakeFloatArray(elems, time); } else if (*typeStr == "string[]") { - auto arr = valueIt->second.get_ptr(); - if (!arr) { + if (!jvalue->is_array()) { error = "value type mismatch, expected array"; goto err; } std::vector elems; - for (auto&& jelem : valueIt->second) { - if (auto v = jelem.get_ptr()) { - elems.emplace_back(*v); + for (auto&& jelem : jvalue->get_array()) { + if (jelem.is_string()) { + elems.push_back(jelem.get_string()); } else { error = "value type mismatch, expected string"; } @@ -580,9 +557,9 @@ std::string ServerStorage::LoadPersistent(std::string_view in) { value = Value::MakeStringArray(std::move(elems), time); } else { // raw - if (auto v = valueIt->second.get_ptr()) { + if (jvalue->is_string()) { std::vector data; - wpi::util::Base64Decode(*v, &data); + wpi::util::Base64Decode(jvalue->get_string(), &data); value = Value::MakeRaw(std::move(data), time); } else { error = "value type mismatch, expected string"; @@ -591,7 +568,7 @@ std::string ServerStorage::LoadPersistent(std::string_view in) { } // create persistent topic - auto topic = CreateTopic(nullptr, *name, *typeStr, props); + auto topic = CreateTopic(nullptr, *name, *typeStr, *props); // set value SetValue(nullptr, topic, value); diff --git a/ntcore/src/main/native/cpp/server/ServerStorage.hpp b/ntcore/src/main/native/cpp/server/ServerStorage.hpp index 3c5756b4ef..e4c9e886e5 100644 --- a/ntcore/src/main/native/cpp/server/ServerStorage.hpp +++ b/ntcore/src/main/native/cpp/server/ServerStorage.hpp @@ -12,10 +12,10 @@ #include "server/ServerTopic.hpp" #include "wpi/util/StringMap.hpp" #include "wpi/util/UidVector.hpp" -#include "wpi/util/json_fwd.hpp" namespace wpi::util { class Logger; +class json; class raw_ostream; } // namespace wpi::util diff --git a/ntcore/src/main/native/cpp/server/ServerTopic.cpp b/ntcore/src/main/native/cpp/server/ServerTopic.cpp index eaf85ddd76..a036b6f048 100644 --- a/ntcore/src/main/native/cpp/server/ServerTopic.cpp +++ b/ntcore/src/main/native/cpp/server/ServerTopic.cpp @@ -13,11 +13,11 @@ bool ServerTopic::SetProperties(const wpi::util::json& update) { return false; } bool updated = false; - for (auto&& elem : update.items()) { - if (elem.value().is_null()) { - properties.erase(elem.key()); + for (auto&& [key, value] : update.get_object()) { + if (value.is_null()) { + properties.erase(key); } else { - properties[elem.key()] = elem.value(); + properties[key] = value; } updated = true; } @@ -32,24 +32,21 @@ void ServerTopic::RefreshProperties() { retained = false; cached = true; - auto persistentIt = properties.find("persistent"); - if (persistentIt != properties.end()) { - if (auto val = persistentIt->get_ptr()) { - persistent = *val; + if (auto prop = properties.lookup("persistent")) { + if (prop->is_bool()) { + persistent = prop->get_bool(); } } - auto retainedIt = properties.find("retained"); - if (retainedIt != properties.end()) { - if (auto val = retainedIt->get_ptr()) { - retained = *val; + if (auto prop = properties.lookup("retained")) { + if (prop->is_bool()) { + retained = prop->get_bool(); } } - auto cachedIt = properties.find("cached"); - if (cachedIt != properties.end()) { - if (auto val = cachedIt->get_ptr()) { - cached = *val; + if (auto prop = properties.lookup("cached")) { + if (prop->is_bool()) { + cached = prop->get_bool(); } } diff --git a/ntcore/src/main/native/include/wpi/nt/ProtobufTopic.hpp b/ntcore/src/main/native/include/wpi/nt/ProtobufTopic.hpp index b4020f0cd9..e449447387 100644 --- a/ntcore/src/main/native/include/wpi/nt/ProtobufTopic.hpp +++ b/ntcore/src/main/native/include/wpi/nt/ProtobufTopic.hpp @@ -15,10 +15,13 @@ #include "wpi/nt/Topic.hpp" #include "wpi/nt/ntcore_cpp.hpp" #include "wpi/util/SmallVector.hpp" -#include "wpi/util/json_fwd.hpp" #include "wpi/util/mutex.hpp" #include "wpi/util/protobuf/Protobuf.hpp" +namespace wpi::util { +class json; +} // namespace wpi::util + namespace wpi::nt { template diff --git a/ntcore/src/main/native/include/wpi/nt/StructArrayTopic.hpp b/ntcore/src/main/native/include/wpi/nt/StructArrayTopic.hpp index 62013b8fd4..18646b9aca 100644 --- a/ntcore/src/main/native/include/wpi/nt/StructArrayTopic.hpp +++ b/ntcore/src/main/native/include/wpi/nt/StructArrayTopic.hpp @@ -19,10 +19,13 @@ #include "wpi/nt/Topic.hpp" #include "wpi/nt/ntcore_cpp.hpp" #include "wpi/util/SmallVector.hpp" -#include "wpi/util/json_fwd.hpp" #include "wpi/util/mutex.hpp" #include "wpi/util/struct/Struct.hpp" +namespace wpi::util { +class json; +} // namespace wpi::util + namespace wpi::nt { template diff --git a/ntcore/src/main/native/include/wpi/nt/StructTopic.hpp b/ntcore/src/main/native/include/wpi/nt/StructTopic.hpp index f910c3c334..4ddae2c280 100644 --- a/ntcore/src/main/native/include/wpi/nt/StructTopic.hpp +++ b/ntcore/src/main/native/include/wpi/nt/StructTopic.hpp @@ -19,9 +19,12 @@ #include "wpi/nt/Topic.hpp" #include "wpi/nt/ntcore_cpp.hpp" #include "wpi/util/SmallVector.hpp" -#include "wpi/util/json_fwd.hpp" #include "wpi/util/struct/Struct.hpp" +namespace wpi::util { +class json; +} // namespace wpi::util + namespace wpi::nt { template diff --git a/ntcore/src/main/native/include/wpi/nt/Topic.hpp b/ntcore/src/main/native/include/wpi/nt/Topic.hpp index edab600d2c..7fe5b0e046 100644 --- a/ntcore/src/main/native/include/wpi/nt/Topic.hpp +++ b/ntcore/src/main/native/include/wpi/nt/Topic.hpp @@ -14,7 +14,10 @@ #include "wpi/nt/NetworkTableType.hpp" #include "wpi/nt/ntcore_c.h" #include "wpi/nt/ntcore_cpp.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpi::nt { diff --git a/ntcore/src/main/native/include/wpi/nt/UnitTopic.hpp b/ntcore/src/main/native/include/wpi/nt/UnitTopic.hpp index 2cd1c0f188..fabbdb5ad3 100644 --- a/ntcore/src/main/native/include/wpi/nt/UnitTopic.hpp +++ b/ntcore/src/main/native/include/wpi/nt/UnitTopic.hpp @@ -369,7 +369,8 @@ class UnitTopic final : public Topic { PublisherType Publish( const PubSubOptions& options = DEFAULT_PUB_SUB_OPTIONS) { return UnitPublisher{::wpi::nt::PublishEx( - m_handle, NT_DOUBLE, "double", {{"unit", T{}.name()}}, options)}; + m_handle, NT_DOUBLE, "double", + wpi::util::json::object("unit", T{}.name()), options)}; } /** diff --git a/ntcore/src/main/native/include/wpi/nt/ntcore_cpp.hpp b/ntcore/src/main/native/include/wpi/nt/ntcore_cpp.hpp index e5b58deb85..5d80bfca53 100644 --- a/ntcore/src/main/native/include/wpi/nt/ntcore_cpp.hpp +++ b/ntcore/src/main/native/include/wpi/nt/ntcore_cpp.hpp @@ -19,9 +19,9 @@ #include "wpi/nt/NetworkTableValue.hpp" #include "wpi/nt/ntcore_c.h" #include "wpi/nt/ntcore_cpp_types.hpp" -#include "wpi/util/json_fwd.hpp" namespace wpi::util { +class json; template class SmallVectorImpl; } // namespace wpi::util diff --git a/ntcore/src/test/native/cpp/LocalStorageTest.cpp b/ntcore/src/test/native/cpp/LocalStorageTest.cpp index 6e81a8549a..95d35101f1 100644 --- a/ntcore/src/test/native/cpp/LocalStorageTest.cpp +++ b/ntcore/src/test/native/cpp/LocalStorageTest.cpp @@ -144,11 +144,12 @@ TEST_F(LocalStorageTest, PublishNewNoPropsNull) { } TEST_F(LocalStorageTest, PublishNew) { - wpi::util::json properties = {{"persistent", true}}; + auto properties = wpi::util::json::object("persistent", true); EXPECT_CALL(network, ClientPublish(_, std::string_view{"foo"}, std::string_view{"boolean"}, properties, IsDefaultPubSubOptions())); - storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {{"persistent", true}}, {}); + storage.Publish(fooTopic, NT_BOOLEAN, "boolean", + wpi::util::json::object("persistent", true), {}); auto info = storage.GetTopicInfo(fooTopic); EXPECT_EQ(info.topic, fooTopic); @@ -645,7 +646,8 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkDefault) { SetupPubSub(false, false); // incoming from the network are treated like a normal local publish - auto topic = storage.ServerAnnounce("foo", 0, "double", {{}}, std::nullopt); + auto topic = storage.ServerAnnounce("foo", 0, "double", + wpi::util::json::object(), std::nullopt); storage.ServerSetValue(topic, val1); storage.ServerSetValue(topic, val2); // verify the timestamp was updated @@ -665,7 +667,8 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepPub) { SetupPubSub(true, false); // incoming from the network are treated like a normal local publish - auto topic = storage.ServerAnnounce("foo", 0, "double", {{}}, std::nullopt); + auto topic = storage.ServerAnnounce("foo", 0, "double", + wpi::util::json::object(), std::nullopt); storage.ServerSetValue(topic, val1); storage.ServerSetValue(topic, val2); // verify the timestamp was updated @@ -684,7 +687,8 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepSub) { SetupPubSub(false, true); // incoming from the network are treated like a normal local publish - auto topic = storage.ServerAnnounce("foo", 0, "double", {{}}, std::nullopt); + auto topic = storage.ServerAnnounce("foo", 0, "double", + wpi::util::json::object(), std::nullopt); storage.ServerSetValue(topic, val1); storage.ServerSetValue(topic, val2); // verify the timestamp was updated @@ -706,7 +710,8 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepPubSub) { SetupPubSub(true, true); // incoming from the network are treated like a normal local publish - auto topic = storage.ServerAnnounce("foo", 0, "double", {{}}, std::nullopt); + auto topic = storage.ServerAnnounce("foo", 0, "double", + wpi::util::json::object(), std::nullopt); storage.ServerSetValue(topic, val1); storage.ServerSetValue(topic, val2); // verify the timestamp was updated diff --git a/ntcore/src/test/native/cpp/StructTest.cpp b/ntcore/src/test/native/cpp/StructTest.cpp index 82d66e40c9..33304556a7 100644 --- a/ntcore/src/test/native/cpp/StructTest.cpp +++ b/ntcore/src/test/native/cpp/StructTest.cpp @@ -342,7 +342,8 @@ TEST_F(StructTest, InnerArrayNonconstexpr) { TEST_F(StructTest, StructA) { wpi::nt::StructTopic topic = inst.GetStructTopic("a"); wpi::nt::StructPublisher pub = topic.Publish(); - wpi::nt::StructPublisher pub2 = topic.PublishEx({{}}); + wpi::nt::StructPublisher pub2 = + topic.PublishEx(wpi::util::json::object()); wpi::nt::StructSubscriber sub = topic.Subscribe({}); wpi::nt::StructEntry entry = topic.GetEntry({}); pub.SetDefault({}); @@ -360,7 +361,8 @@ TEST_F(StructTest, StructArrayA) { wpi::nt::StructArrayTopic topic = inst.GetStructArrayTopic("a"); wpi::nt::StructArrayPublisher pub = topic.Publish(); - wpi::nt::StructArrayPublisher pub2 = topic.PublishEx({{}}); + wpi::nt::StructArrayPublisher pub2 = + topic.PublishEx(wpi::util::json::object()); wpi::nt::StructArraySubscriber sub = topic.Subscribe({}); wpi::nt::StructArrayEntry entry = topic.GetEntry({}); pub.SetDefault({{ThingA{}, ThingA{}}}); @@ -378,7 +380,8 @@ TEST_F(StructTest, StructFixedArrayA) { wpi::nt::StructTopic> topic = inst.GetStructTopic>("a"); wpi::nt::StructPublisher> pub = topic.Publish(); - wpi::nt::StructPublisher> pub2 = topic.PublishEx({{}}); + wpi::nt::StructPublisher> pub2 = + topic.PublishEx(wpi::util::json::object()); wpi::nt::StructSubscriber> sub = topic.Subscribe({}); wpi::nt::StructEntry> entry = topic.GetEntry({}); std::array arr; @@ -398,7 +401,8 @@ TEST_F(StructTest, StructB) { wpi::nt::StructTopic topic = inst.GetStructTopic("b", info); wpi::nt::StructPublisher pub = topic.Publish(); - wpi::nt::StructPublisher pub2 = topic.PublishEx({{}}); + wpi::nt::StructPublisher pub2 = + topic.PublishEx(wpi::util::json::object()); wpi::nt::StructSubscriber sub = topic.Subscribe({}); wpi::nt::StructEntry entry = topic.GetEntry({}); pub.SetDefault({}); @@ -417,7 +421,8 @@ TEST_F(StructTest, StructArrayB) { wpi::nt::StructArrayTopic topic = inst.GetStructArrayTopic("b", info); wpi::nt::StructArrayPublisher pub = topic.Publish(); - wpi::nt::StructArrayPublisher pub2 = topic.PublishEx({{}}); + wpi::nt::StructArrayPublisher pub2 = + topic.PublishEx(wpi::util::json::object()); wpi::nt::StructArraySubscriber sub = topic.Subscribe({}); wpi::nt::StructArrayEntry entry = topic.GetEntry({}); pub.SetDefault({{ThingB{}, ThingB{}}}); @@ -437,7 +442,7 @@ TEST_F(StructTest, StructFixedArrayB) { inst.GetStructTopic, Info1>("b", info); wpi::nt::StructPublisher, Info1> pub = topic.Publish(); wpi::nt::StructPublisher, Info1> pub2 = - topic.PublishEx({{}}); + topic.PublishEx(wpi::util::json::object()); wpi::nt::StructSubscriber, Info1> sub = topic.Subscribe({}); wpi::nt::StructEntry, Info1> entry = topic.GetEntry({}); diff --git a/ntcore/src/test/native/cpp/net/WireDecoderTest.cpp b/ntcore/src/test/native/cpp/net/WireDecoderTest.cpp index 517eaa1839..b71e6d4c52 100644 --- a/ntcore/src/test/native/cpp/net/WireDecoderTest.cpp +++ b/ntcore/src/test/native/cpp/net/WireDecoderTest.cpp @@ -43,33 +43,20 @@ TEST_F(WireDecodeTextClientTest, EmptyArray) { } TEST_F(WireDecodeTextClientTest, ErrorEmpty) { - EXPECT_CALL( - logger, - Call(_, _, _, - "could not decode JSON message: [json.exception.parse_error.101] " - "parse error at line 1, column 1: attempting to parse an empty " - "input; check that your input string or stream contains the " - "expected JSON"sv)); + EXPECT_CALL(logger, + Call(_, _, _, "could not decode JSON message: absent_value"sv)); net::WireDecodeText("", handler, logger); } TEST_F(WireDecodeTextClientTest, ErrorBadJson1) { - EXPECT_CALL( - logger, - Call(_, _, _, - "could not decode JSON message: [json.exception.parse_error.101] " - "parse error at line 1, column 2: syntax error while parsing value " - "- unexpected end of input; expected '[', '{', or a literal"sv)); + EXPECT_CALL(logger, + Call(_, _, _, "could not decode JSON message: unexpected_eof"sv)); net::WireDecodeText("[", handler, logger); } TEST_F(WireDecodeTextClientTest, ErrorBadJson2) { - EXPECT_CALL( - logger, - Call(_, _, _, - "could not decode JSON message: [json.exception.parse_error.101] " - "parse error at line 1, column 3: syntax error while parsing object " - "key - unexpected end of input; expected string literal"sv)); + EXPECT_CALL(logger, + Call(_, _, _, "could not decode JSON message: unexpected_eof"sv)); net::WireDecodeText("[{", handler, logger); } @@ -129,7 +116,7 @@ TEST_F(WireDecodeTextClientTest, PublishPropsEmpty) { } TEST_F(WireDecodeTextClientTest, PublishProps) { - wpi::util::json props = {{"k", 6}}; + auto props = wpi::util::json::object("k", 6); EXPECT_CALL(handler, ClientPublish(5, std::string_view{"test"}, std::string_view{"double"}, props, PubSubOptionsEq({}))); diff --git a/ntcore/src/test/native/cpp/net/WireEncoderTest.cpp b/ntcore/src/test/native/cpp/net/WireEncoderTest.cpp index e8bdf9134e..a80cafb5cb 100644 --- a/ntcore/src/test/native/cpp/net/WireEncoderTest.cpp +++ b/ntcore/src/test/native/cpp/net/WireEncoderTest.cpp @@ -29,7 +29,9 @@ class WireEncoderTextTest : public ::testing::Test { protected: std::string out; wpi::util::raw_string_ostream os{out}; - wpi::util::json GetJson() { return wpi::util::json::parse(os.str()); } + wpi::util::json GetJson() { + return wpi::util::json::parse(os.str()).value_or(wpi::util::json::object()); + } }; class WireEncoderBinaryTest : public ::testing::Test { @@ -47,7 +49,8 @@ TEST_F(WireEncoderTextTest, PublishPropsEmpty) { } TEST_F(WireEncoderTextTest, PublishProps) { - net::WireEncodePublish(os, 5, "test", "double", {{"k", 6}}); + net::WireEncodePublish(os, 5, "test", "double", + wpi::util::json::object("k", 6)); ASSERT_EQ(os.str(), "{\"method\":\"publish\",\"params\":{" "\"name\":\"test\",\"properties\":{\"k\":6}," @@ -60,7 +63,7 @@ TEST_F(WireEncoderTextTest, Unpublish) { } TEST_F(WireEncoderTextTest, SetProperties) { - net::WireEncodeSetProperties(os, "test", {{"k", 6}}); + net::WireEncodeSetProperties(os, "test", wpi::util::json::object("k", 6)); ASSERT_EQ(os.str(), "{\"method\":\"setproperties\",\"params\":{" "\"name\":\"test\",\"update\":{\"k\":6}}}"); @@ -122,7 +125,8 @@ TEST_F(WireEncoderTextTest, Announce) { } TEST_F(WireEncoderTextTest, AnnounceProperties) { - net::WireEncodeAnnounce(os, "test", 5, "double", {{"k", 6}}, std::nullopt); + net::WireEncodeAnnounce(os, "test", 5, "double", + wpi::util::json::object("k", 6), std::nullopt); ASSERT_EQ(os.str(), "{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\"," "\"properties\":{\"k\":6},\"type\":\"double\"}}"); @@ -144,7 +148,8 @@ TEST_F(WireEncoderTextTest, Unannounce) { } TEST_F(WireEncoderTextTest, MessagePublish) { - net::ClientMessage msg{net::PublishMsg{5, "test", "double", {{"k", 6}}, {}}}; + net::ClientMessage msg{net::PublishMsg{ + 5, "test", "double", wpi::util::json::object("k", 6), {}}}; ASSERT_TRUE(net::WireEncodeText(os, msg)); ASSERT_EQ(os.str(), "{\"method\":\"publish\",\"params\":{" @@ -159,7 +164,8 @@ TEST_F(WireEncoderTextTest, MessageUnpublish) { } TEST_F(WireEncoderTextTest, MessageSetProperties) { - net::ClientMessage msg{net::SetPropertiesMsg{"test", {{"k", 6}}}}; + net::ClientMessage msg{ + net::SetPropertiesMsg{"test", wpi::util::json::object("k", 6)}}; ASSERT_TRUE(net::WireEncodeText(os, msg)); ASSERT_EQ(os.str(), "{\"method\":\"setproperties\",\"params\":{" @@ -190,8 +196,8 @@ TEST_F(WireEncoderTextTest, MessageAnnounce) { } TEST_F(WireEncoderTextTest, MessageAnnounceProperties) { - net::ServerMessage msg{ - net::AnnounceMsg{"test", 5, "double", std::nullopt, {{"k", 6}}}}; + net::ServerMessage msg{net::AnnounceMsg{"test", 5, "double", std::nullopt, + wpi::util::json::object("k", 6)}}; ASSERT_TRUE(net::WireEncodeText(os, msg)); ASSERT_EQ(os.str(), "{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\"," diff --git a/simulation/halsim_ws_client/src/main/native/cpp/HALSimWS.cpp b/simulation/halsim_ws_client/src/main/native/cpp/HALSimWS.cpp index 2e685b622d..975ba4298a 100644 --- a/simulation/halsim_ws_client/src/main/native/cpp/HALSimWS.cpp +++ b/simulation/halsim_ws_client/src/main/native/cpp/HALSimWS.cpp @@ -177,8 +177,8 @@ void HALSimWS::OnNetValueChanged(const wpi::util::json& msg) { // generate the key try { - auto& type = msg.at("type").get_ref(); - auto& device = msg.at("device").get_ref(); + auto& type = msg.at("type").get_string(); + auto& device = msg.at("device").get_string(); wpi::util::SmallString<64> key; key.append(type); @@ -191,7 +191,7 @@ void HALSimWS::OnNetValueChanged(const wpi::util::json& msg) { if (provider) { provider->OnNetValueChanged(msg.at("data")); } - } catch (wpi::util::json::exception& e) { + } catch (std::logic_error& e) { wpi::util::print(stderr, "Error with incoming message: {}\n", e.what()); } } diff --git a/simulation/halsim_ws_client/src/main/native/cpp/HALSimWSClientConnection.cpp b/simulation/halsim_ws_client/src/main/native/cpp/HALSimWSClientConnection.cpp index 57bd42f0d8..41c4e8b4e9 100644 --- a/simulation/halsim_ws_client/src/main/native/cpp/HALSimWSClientConnection.cpp +++ b/simulation/halsim_ws_client/src/main/native/cpp/HALSimWSClientConnection.cpp @@ -48,18 +48,15 @@ void HALSimWSClientConnection::Initialize() { return; } - wpi::util::json j; - try { - j = wpi::util::json::parse(msg); - } catch (const wpi::util::json::parse_error& e) { - std::string err("JSON parse failed: "); - err += e.what(); + auto j = wpi::util::json::parse(msg); + if (!j) { + auto err = fmt::format("JSON parse failed: {}", j.error()); wpi::util::print(stderr, "{}\n", err); m_websocket->Fail(1003, err); return; } - m_client->OnNetValueChanged(j); + m_client->OnNetValueChanged(*j); }); m_websocket->closed.connect([this](uint16_t, auto) { @@ -79,11 +76,11 @@ void HALSimWSClientConnection::OnSimValueChanged(const wpi::util::json& msg) { // Skip sending if this message is not in the allowed filter list try { - auto& type = msg.at("type").get_ref(); + auto& type = msg.at("type").get_string(); if (!m_client->CanSendMessage(type)) { return; } - } catch (wpi::util::json::exception& e) { + } catch (std::logic_error& e) { wpi::util::print(stderr, "Error with message: {}\n", e.what()); } diff --git a/simulation/halsim_ws_client/src/main/native/include/wpi/halsim/ws_client/HALSimWS.hpp b/simulation/halsim_ws_client/src/main/native/include/wpi/halsim/ws_client/HALSimWS.hpp index ca8ec6df2a..c00e12ea59 100644 --- a/simulation/halsim_ws_client/src/main/native/include/wpi/halsim/ws_client/HALSimWS.hpp +++ b/simulation/halsim_ws_client/src/main/native/include/wpi/halsim/ws_client/HALSimWS.hpp @@ -16,7 +16,10 @@ #include "wpi/net/uv/Tcp.hpp" #include "wpi/net/uv/Timer.hpp" #include "wpi/util/StringMap.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpilibws { diff --git a/simulation/halsim_ws_client/src/main/native/include/wpi/halsim/ws_client/HALSimWSClientConnection.hpp b/simulation/halsim_ws_client/src/main/native/include/wpi/halsim/ws_client/HALSimWSClientConnection.hpp index 25cde92214..79e9ec21cb 100644 --- a/simulation/halsim_ws_client/src/main/native/include/wpi/halsim/ws_client/HALSimWSClientConnection.hpp +++ b/simulation/halsim_ws_client/src/main/native/include/wpi/halsim/ws_client/HALSimWSClientConnection.hpp @@ -12,8 +12,10 @@ #include "wpi/net/WebSocket.hpp" #include "wpi/net/uv/Buffer.hpp" #include "wpi/net/uv/Stream.hpp" -#include "wpi/util/json_fwd.hpp" -#include "wpi/util/mutex.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpilibws { diff --git a/simulation/halsim_ws_core/src/main/native/cpp/WSHalProviders.cpp b/simulation/halsim_ws_core/src/main/native/cpp/WSHalProviders.cpp index bd8b83dfa5..b39957307f 100644 --- a/simulation/halsim_ws_core/src/main/native/cpp/WSHalProviders.cpp +++ b/simulation/halsim_ws_core/src/main/native/cpp/WSHalProviders.cpp @@ -27,8 +27,10 @@ void HALSimWSHalProvider::OnNetworkDisconnected() { void HALSimWSHalProvider::ProcessHalCallback(const wpi::util::json& payload) { auto ws = m_ws.lock(); if (ws) { - wpi::util::json netValue = { - {"type", m_type}, {"device", m_deviceId}, {"data", payload}}; + auto netValue = wpi::util::json::object(); + netValue["type"] = m_type; + netValue["device"] = m_deviceId; + netValue["data"] = payload; ws->OnSimValueChanged(netValue); } } diff --git a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_AddressableLED.cpp b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_AddressableLED.cpp index 4b4baa80bb..caa35f67b3 100644 --- a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_AddressableLED.cpp +++ b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_AddressableLED.cpp @@ -4,19 +4,20 @@ #include "wpi/halsim/ws_core/WSProvider_AddressableLED.hpp" +#include #include #include "wpi/hal/Ports.h" #include "wpi/hal/simulation/AddressableLEDData.h" -#define REGISTER(halsim, jsonid, ctype, haltype) \ - HALSIM_RegisterAddressableLED##halsim##Callback( \ - m_channel, \ - [](const char* name, void* param, const struct HAL_Value* value) { \ - static_cast(param) \ - ->ProcessHalCallback( \ - {{jsonid, static_cast(value->data.v_##haltype)}}); \ - }, \ +#define REGISTER(halsim, jsonid, ctype, haltype) \ + HALSIM_RegisterAddressableLED##halsim##Callback( \ + m_channel, \ + [](const char* name, void* param, const struct HAL_Value* value) { \ + static_cast(param) \ + ->ProcessHalCallback(wpi::util::json::object( \ + jsonid, static_cast(value->data.v_##haltype))); \ + }, \ this, true) namespace wpilibws { void HALSimWSProviderAddressableLED::Initialize( @@ -43,15 +44,17 @@ void HALSimWSProviderAddressableLED::RegisterCallbacks() { const HAL_AddressableLEDData* data = reinterpret_cast(buffer); - std::vector jsonData; + auto jsonData = wpi::util::json::array(); for (size_t i = 0; i < numLeds; ++i) { - jsonData.push_back( - {{"r", data[i].r}, {"g", data[i].g}, {"b", data[i].b}}); + jsonData.emplace_back( + wpi::util::json::object("r", static_cast(data[i].r), "g", + static_cast(data[i].g), "b", + static_cast(data[i].b))); } wpi::util::json payload; - payload["ProcessHalCallback(payload); }, diff --git a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Analog.cpp b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Analog.cpp index 423fe8a570..657d7d6f14 100644 --- a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Analog.cpp +++ b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Analog.cpp @@ -12,7 +12,8 @@ m_channel, \ [](const char* name, void* param, const struct HAL_Value* value) { \ static_cast(param)->ProcessHalCallback( \ - {{jsonid, static_cast(value->data.v_##haltype)}}); \ + wpi::util::json::object( \ + jsonid, static_cast(value->data.v_##haltype))); \ }, \ this, true) @@ -54,9 +55,8 @@ void HALSimWSProviderAnalogIn::DoCancelCallbacks() { } void HALSimWSProviderAnalogIn::OnNetValueChanged(const wpi::util::json& json) { - wpi::util::json::const_iterator it; - if ((it = json.find(">voltage")) != json.end()) { - HALSIM_SetAnalogInVoltage(m_channel, it.value()); + if (auto val = json.lookup(">voltage"); val && val->is_number()) { + HALSIM_SetAnalogInVoltage(m_channel, val->get_number()); } } diff --git a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_DIO.cpp b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_DIO.cpp index 68a86632b5..6157a039cb 100644 --- a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_DIO.cpp +++ b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_DIO.cpp @@ -12,7 +12,8 @@ m_channel, \ [](const char* name, void* param, const struct HAL_Value* value) { \ static_cast(param)->ProcessHalCallback( \ - {{jsonid, static_cast(value->data.v_##haltype)}}); \ + wpi::util::json::object( \ + jsonid, static_cast(value->data.v_##haltype))); \ }, \ this, true) @@ -51,9 +52,8 @@ void HALSimWSProviderDIO::DoCancelCallbacks() { } void HALSimWSProviderDIO::OnNetValueChanged(const wpi::util::json& json) { - wpi::util::json::const_iterator it; - if ((it = json.find("<>value")) != json.end()) { - HALSIM_SetDIOValue(m_channel, static_cast(it.value())); + if (auto val = json.lookup("<>value"); val && val->is_bool()) { + HALSIM_SetDIOValue(m_channel, val->get_bool()); } } diff --git a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_DriverStation.cpp b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_DriverStation.cpp index ff10f68ac1..b0b8fe3031 100644 --- a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_DriverStation.cpp +++ b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_DriverStation.cpp @@ -12,13 +12,13 @@ #include "wpi/hal/simulation/DriverStationData.h" #include "wpi/util/string.hpp" -#define REGISTER(halsim, jsonid, ctype, haltype) \ - HALSIM_RegisterDriverStation##halsim##Callback( \ - [](const char* name, void* param, const struct HAL_Value* value) { \ - static_cast(param) \ - ->ProcessHalCallback( \ - {{jsonid, static_cast(value->data.v_##haltype)}}); \ - }, \ +#define REGISTER(halsim, jsonid, ctype, haltype) \ + HALSIM_RegisterDriverStation##halsim##Callback( \ + [](const char* name, void* param, const struct HAL_Value* value) { \ + static_cast(param) \ + ->ProcessHalCallback(wpi::util::json::object( \ + jsonid, static_cast(value->data.v_##haltype))); \ + }, \ this, true) namespace wpilibws { @@ -55,7 +55,7 @@ void HALSimWSProviderDriverStation::RegisterCallbacks() { m_newDataCbKey = HALSIM_RegisterDriverStationNewDataCallback( [](const char* name, void* param, const struct HAL_Value* value) { static_cast(param)->ProcessHalCallback( - {{">new_data", true}}); + wpi::util::json::object(">new_data", true)); }, this, true); @@ -86,7 +86,7 @@ void HALSimWSProviderDriverStation::RegisterCallbacks() { break; } static_cast(param)->ProcessHalCallback( - {{">station", station}}); + wpi::util::json::object(">station", station)); }, this, true); @@ -124,25 +124,25 @@ void HALSimWSProviderDriverStation::OnNetValueChanged( return; } - wpi::util::json::const_iterator it; - if ((it = json.find(">enabled")) != json.end()) { - HALSIM_SetDriverStationEnabled(it.value()); + if (auto val = json.lookup(">enabled"); val && val->is_bool()) { + HALSIM_SetDriverStationEnabled(val->get_bool()); } - if ((it = json.find(">robotMode")) != json.end()) { - HALSIM_SetDriverStationRobotMode(it.value()); + if (auto val = json.lookup(">robotMode"); val && val->is_int()) { + HALSIM_SetDriverStationRobotMode( + static_cast(val->get_int())); } - if ((it = json.find(">estop")) != json.end()) { - HALSIM_SetDriverStationEStop(it.value()); + if (auto val = json.lookup(">estop"); val && val->is_bool()) { + HALSIM_SetDriverStationEStop(val->get_bool()); } - if ((it = json.find(">fms")) != json.end()) { - HALSIM_SetDriverStationFmsAttached(it.value()); + if (auto val = json.lookup(">fms"); val && val->is_bool()) { + HALSIM_SetDriverStationFmsAttached(val->get_bool()); } - if ((it = json.find(">ds")) != json.end()) { - HALSIM_SetDriverStationDsAttached(it.value()); + if (auto val = json.lookup(">ds"); val && val->is_bool()) { + HALSIM_SetDriverStationDsAttached(val->get_bool()); } - if ((it = json.find(">station")) != json.end()) { - auto& station = it.value().get_ref(); + if (auto val = json.lookup(">station"); val && val->is_string()) { + auto& station = val->get_string(); if (station == "red1") { HALSIM_SetDriverStationAllianceStationId(HAL_ALLIANCE_STATION_RED_1); } else if (station == "red2") { @@ -158,17 +158,17 @@ void HALSimWSProviderDriverStation::OnNetValueChanged( } } - if ((it = json.find(">match_time")) != json.end()) { - HALSIM_SetDriverStationMatchTime(it.value()); + if (auto val = json.lookup(">match_time"); val && val->is_number()) { + HALSIM_SetDriverStationMatchTime(val->get_number()); } - if ((it = json.find(">game_data")) != json.end()) { - std::string message = it.value().get_ref(); + if (auto val = json.lookup(">game_data"); val && val->is_string()) { + std::string message = val->get_string(); auto str = wpi::util::make_string(message); HALSIM_SetGameDataString(&str); } // Only notify usercode if we get the new data message - if ((it = json.find(">new_data")) != json.end()) { + if (json.contains(">new_data")) { HALSIM_NotifyDriverStationNewData(); } } diff --git a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Encoder.cpp b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Encoder.cpp index 0d1dd33ada..8d13e2fb9c 100644 --- a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Encoder.cpp +++ b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Encoder.cpp @@ -12,7 +12,8 @@ m_channel, \ [](const char* name, void* param, const struct HAL_Value* value) { \ static_cast(param)->ProcessHalCallback( \ - {{jsonid, static_cast(value->data.v_##haltype)}}); \ + wpi::util::json::object( \ + jsonid, static_cast(value->data.v_##haltype))); \ }, \ this, true) @@ -36,7 +37,7 @@ void HALSimWSProviderEncoder::RegisterCallbacks() { auto provider = static_cast(param); bool init = static_cast(value->data.v_boolean); - wpi::util::json payload = {{"(param); - provider->ProcessHalCallback( - {{">count", static_cast(value->data.v_int + - provider->m_countOffset)}}); + provider->ProcessHalCallback(wpi::util::json::object( + ">count", + static_cast(value->data.v_int + provider->m_countOffset))); }, this, true); m_periodCbKey = REGISTER(Period, ">period", double, double); @@ -96,13 +97,12 @@ void HALSimWSProviderEncoder::DoCancelCallbacks() { } void HALSimWSProviderEncoder::OnNetValueChanged(const wpi::util::json& json) { - wpi::util::json::const_iterator it; - if ((it = json.find(">count")) != json.end()) { - HALSIM_SetEncoderCount(m_channel, - static_cast(it.value()) - m_countOffset); + if (auto val = json.lookup(">count"); val && val->is_int()) { + HALSIM_SetEncoderCount(m_channel, val->get_int() - m_countOffset); } - if ((it = json.find(">period")) != json.end()) { - HALSIM_SetEncoderPeriod(m_channel, it.value()); + + if (auto val = json.lookup(">period"); val && val->is_number()) { + HALSIM_SetEncoderPeriod(m_channel, val->get_number()); } } diff --git a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_HAL.cpp b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_HAL.cpp index 778b20fe3c..2f0214153f 100644 --- a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_HAL.cpp +++ b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_HAL.cpp @@ -27,14 +27,14 @@ void HALSimWSProviderHAL::RegisterCallbacks() { m_simPeriodicBeforeCbKey = HALSIM_RegisterSimPeriodicBeforeCallback( [](void* param) { static_cast(param)->ProcessHalCallback( - {{">sim_periodic_before", true}}); + wpi::util::json::object(">sim_periodic_before", true)); }, this); m_simPeriodicAfterCbKey = HALSIM_RegisterSimPeriodicAfterCallback( [](void* param) { static_cast(param)->ProcessHalCallback( - {{">sim_periodic_after", true}}); + wpi::util::json::object(">sim_periodic_after", true)); }, this); } diff --git a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Joystick.cpp b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Joystick.cpp index 9411f2df6a..040eab3d39 100644 --- a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Joystick.cpp +++ b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Joystick.cpp @@ -8,6 +8,7 @@ #include #include +#include "wpi/hal/DriverStationTypes.h" #include "wpi/hal/Ports.h" #include "wpi/hal/simulation/DriverStationData.h" @@ -103,31 +104,34 @@ void HALSimWSProviderJoystick::OnNetValueChanged(const wpi::util::json& json) { return; } - wpi::util::json::const_iterator it; - if ((it = json.find(">axes")) != json.end()) { + if (auto val = json.lookup(">axes"); val && val->is_array()) { + auto& arr = val->get_array(); HAL_JoystickAxes axes{}; - wpi::util::json::size_type axesCount = std::min( - it.value().size(), - static_cast(HAL_MAX_JOYSTICK_AXES)); + size_t axesCount = + std::min(arr.size(), static_cast(HAL_MAX_JOYSTICK_AXES)); axes.available = (1 << axesCount) - 1; - for (wpi::util::json::size_type i = 0; i < axesCount; i++) { - axes.axes[i] = it.value()[i]; + for (size_t i = 0; i < axesCount; i++) { + if (arr[i].is_number()) { + axes.axes[i] = arr[i].get_number(); + } else { + axes.axes[i] = 0.0; + } } HALSIM_SetJoystickAxes(m_channel, &axes); } - if ((it = json.find(">buttons")) != json.end()) { + if (auto val = json.lookup(">buttons"); val && val->is_array()) { HAL_JoystickButtons buttons{}; - wpi::util::json::size_type buttonsCount = std::min( - it.value().size(), static_cast(64)); + auto& arr = val->get_array(); + size_t buttonsCount = std::min(arr.size(), static_cast(64)); if (buttonsCount < 64) { buttons.available = (1ULL << buttonsCount) - 1; } else { buttons.available = (std::numeric_limits::max)(); } - for (wpi::util::json::size_type i = 0; i < buttonsCount; i++) { - if (it.value()[i]) { + for (size_t i = 0; i < buttonsCount; i++) { + if (arr[i].is_bool() && arr[i].get_bool()) { buttons.buttons |= 1llu << i; } } @@ -135,14 +139,18 @@ void HALSimWSProviderJoystick::OnNetValueChanged(const wpi::util::json& json) { HALSIM_SetJoystickButtons(m_channel, &buttons); } - if ((it = json.find(">povs")) != json.end()) { + if (auto val = json.lookup(">povs"); val && val->is_array()) { HAL_JoystickPOVs povs{}; - wpi::util::json::size_type povsCount = std::min( - it.value().size(), - static_cast(HAL_MAX_JOYSTICK_POVS)); + auto& arr = val->get_array(); + size_t povsCount = + std::min(arr.size(), static_cast(HAL_MAX_JOYSTICK_POVS)); povs.available = (1 << povsCount) - 1; - for (wpi::util::json::size_type i = 0; i < povsCount; i++) { - povs.povs[i] = it.value()[i]; + for (size_t i = 0; i < povsCount; i++) { + if (arr[i].is_int()) { + povs.povs[i] = static_cast(arr[i].get_int()); + } else { + povs.povs[i] = HAL_JOYSTICK_POV_CENTERED; + } } HALSIM_SetJoystickPOVs(m_channel, &povs); diff --git a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_PCM.cpp b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_PCM.cpp index d158b4f803..347d60af78 100644 --- a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_PCM.cpp +++ b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_PCM.cpp @@ -12,7 +12,8 @@ m_channel, \ [](const char* name, void* param, const struct HAL_Value* value) { \ static_cast(param)->ProcessHalCallback( \ - {{jsonid, static_cast(value->data.v_##haltype)}}); \ + wpi::util::json::object( \ + jsonid, static_cast(value->data.v_##haltype))); \ }, \ this, true) namespace wpilibws { diff --git a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_PWM.cpp b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_PWM.cpp index d49f19a78f..4d27a7a650 100644 --- a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_PWM.cpp +++ b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_PWM.cpp @@ -12,7 +12,8 @@ m_channel, \ [](const char* name, void* param, const struct HAL_Value* value) { \ static_cast(param)->ProcessHalCallback( \ - {{jsonid, static_cast(value->data.v_##haltype)}}); \ + wpi::util::json::object( \ + jsonid, static_cast(value->data.v_##haltype))); \ }, \ this, true) namespace wpilibws { diff --git a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_RoboRIO.cpp b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_RoboRIO.cpp index 3f660b2dfd..55437b02c6 100644 --- a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_RoboRIO.cpp +++ b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_RoboRIO.cpp @@ -11,7 +11,8 @@ HALSIM_RegisterRoboRio##halsim##Callback( \ [](const char* name, void* param, const struct HAL_Value* value) { \ static_cast(param)->ProcessHalCallback( \ - {{jsonid, static_cast(value->data.v_##haltype)}}); \ + wpi::util::json::object( \ + jsonid, static_cast(value->data.v_##haltype))); \ }, \ this, true) @@ -54,22 +55,20 @@ void HALSimWSProviderRoboRIO::DoCancelCallbacks() { } void HALSimWSProviderRoboRIO::OnNetValueChanged(const wpi::util::json& json) { - wpi::util::json::const_iterator it; - if ((it = json.find(">vin_voltage")) != json.end()) { - HALSIM_SetRoboRioVInVoltage(it.value()); + if (auto val = json.lookup(">vin_voltage"); val && val->is_number()) { + HALSIM_SetRoboRioVInVoltage(val->get_number()); } - - if ((it = json.find(">3v3_voltage")) != json.end()) { - HALSIM_SetRoboRioUserVoltage3V3(it.value()); + if (auto val = json.lookup(">3v3_voltage"); val && val->is_number()) { + HALSIM_SetRoboRioUserVoltage3V3(val->get_number()); } - if ((it = json.find(">3v3_current")) != json.end()) { - HALSIM_SetRoboRioUserCurrent3V3(it.value()); + if (auto val = json.lookup(">3v3_current"); val && val->is_number()) { + HALSIM_SetRoboRioUserCurrent3V3(val->get_number()); } - if ((it = json.find(">3v3_active")) != json.end()) { - HALSIM_SetRoboRioUserActive3V3(static_cast(it.value())); + if (auto val = json.lookup(">3v3_active"); val && val->is_bool()) { + HALSIM_SetRoboRioUserActive3V3(val->get_bool()); } - if ((it = json.find(">3v3_faults")) != json.end()) { - HALSIM_SetRoboRioUserFaults3V3(it.value()); + if (auto val = json.lookup(">3v3_faults"); val && val->is_number()) { + HALSIM_SetRoboRioUserFaults3V3(val->get_number()); } } diff --git a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_SimDevice.cpp b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_SimDevice.cpp index b2e4b21819..7d21e98b31 100644 --- a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_SimDevice.cpp +++ b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_SimDevice.cpp @@ -62,53 +62,59 @@ void HALSimWSProviderSimDevice::CancelCallbacks() { } void HALSimWSProviderSimDevice::OnNetValueChanged(const wpi::util::json& json) { - auto it = json.cbegin(); - auto end = json.cend(); - std::shared_lock lock(m_vhLock); - for (; it != end; ++it) { - auto vd = m_valueHandles.find(it.key()); + for (auto&& [key, jvalue] : json.get_object()) { + auto vd = m_valueHandles.find(key); if (vd != m_valueHandles.end()) { HAL_Value value; value.type = vd->second.valueType; switch (value.type) { case HAL_BOOLEAN: - value.data.v_boolean = static_cast(it.value()) ? 1 : 0; + if (jvalue.is_bool()) { + value.data.v_boolean = jvalue.get_bool() ? 1 : 0; + } break; case HAL_DOUBLE: - value.data.v_double = it.value(); - value.data.v_double -= vd->second.doubleOffset; + if (jvalue.is_number()) { + value.data.v_double = jvalue.get_double(); + value.data.v_double -= vd->second.doubleOffset; + } break; case HAL_ENUM: { - if (it->is_string()) { + if (jvalue.is_string()) { auto& options = vd->second.options; - auto& str = it.value().get_ref(); + auto& str = jvalue.get_string(); auto optionIt = std::find_if(options.begin(), options.end(), [&](const std::string& v) { return v == str; }); if (optionIt != options.end()) { value.data.v_enum = optionIt - options.begin(); } - } else if (it->is_number()) { + } else if (jvalue.is_float() || jvalue.is_double()) { auto& values = vd->second.optionValues; - double num = it.value(); + double num = jvalue.get_number(); auto valueIt = std::find_if( values.begin(), values.end(), [&](double v) { return std::fabs(v - num) < 1e-4; }); if (valueIt != values.end()) { value.data.v_enum = valueIt - values.begin(); } + } else if (jvalue.is_int()) { + value.data.v_enum = jvalue.get_int(); } - value.data.v_enum = it.value(); break; } case HAL_INT: - value.data.v_int = it.value(); - value.data.v_int -= vd->second.intOffset; + if (jvalue.is_int()) { + value.data.v_int = jvalue.get_int(); + value.data.v_int -= vd->second.intOffset; + } break; case HAL_LONG: - value.data.v_long = it.value(); - value.data.v_long -= vd->second.intOffset; + if (jvalue.is_int()) { + value.data.v_long = jvalue.get_int(); + value.data.v_long -= vd->second.intOffset; + } break; default: break; @@ -193,29 +199,31 @@ void HALSimWSProviderSimDevice::OnValueChanged(SimDeviceValueData* valueData, if (ws) { switch (value->type) { case HAL_BOOLEAN: - ProcessHalCallback( - {{valueData->key, static_cast(value->data.v_boolean)}}); + ProcessHalCallback(wpi::util::json::object( + valueData->key, static_cast(value->data.v_boolean))); break; case HAL_DOUBLE: - ProcessHalCallback( - {{valueData->key, value->data.v_double + valueData->doubleOffset}}); + ProcessHalCallback(wpi::util::json::object( + valueData->key, value->data.v_double + valueData->doubleOffset)); break; case HAL_ENUM: { int v = value->data.v_enum; if (v >= 0 && v < static_cast(valueData->optionValues.size())) { - ProcessHalCallback({{valueData->key, valueData->optionValues[v]}}); + ProcessHalCallback(wpi::util::json::object( + valueData->key, valueData->optionValues[v])); } else if (v >= 0 && v < static_cast(valueData->options.size())) { - ProcessHalCallback({{valueData->key, valueData->options[v]}}); + ProcessHalCallback( + wpi::util::json::object(valueData->key, valueData->options[v])); } break; } case HAL_INT: - ProcessHalCallback( - {{valueData->key, value->data.v_int + valueData->intOffset}}); + ProcessHalCallback(wpi::util::json::object( + valueData->key, value->data.v_int + valueData->intOffset)); break; case HAL_LONG: - ProcessHalCallback( - {{valueData->key, value->data.v_long + valueData->intOffset}}); + ProcessHalCallback(wpi::util::json::object( + valueData->key, value->data.v_long + valueData->intOffset)); break; default: break; @@ -254,8 +262,10 @@ void HALSimWSProviderSimDevice::ProcessHalCallback( const wpi::util::json& payload) { auto ws = m_ws.lock(); if (ws) { - wpi::util::json netValue = { - {"type", m_type}, {"device", m_deviceId}, {"data", payload}}; + auto netValue = wpi::util::json::object(); + netValue["type"] = m_type; + netValue["device"] = m_deviceId; + netValue["data"] = payload; ws->OnSimValueChanged(netValue); } } diff --git a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Solenoid.cpp b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Solenoid.cpp index d00f4ce538..7d70e8a525 100644 --- a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Solenoid.cpp +++ b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_Solenoid.cpp @@ -18,7 +18,8 @@ m_pcmIndex, m_solenoidIndex, \ [](const char* name, void* param, const struct HAL_Value* value) { \ static_cast(param)->ProcessHalCallback( \ - {{jsonid, static_cast(value->data.v_##haltype)}}); \ + wpi::util::json::object( \ + jsonid, static_cast(value->data.v_##haltype))); \ }, \ this, true) diff --git a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_dPWM.cpp b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_dPWM.cpp index 47fbcf63be..435c12d56f 100644 --- a/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_dPWM.cpp +++ b/simulation/halsim_ws_core/src/main/native/cpp/WSProvider_dPWM.cpp @@ -12,7 +12,8 @@ m_channel, \ [](const char* name, void* param, const struct HAL_Value* value) { \ static_cast(param)->ProcessHalCallback( \ - {{jsonid, static_cast(value->data.v_##haltype)}}); \ + wpi::util::json::object( \ + jsonid, static_cast(value->data.v_##haltype))); \ }, \ this, true) diff --git a/simulation/halsim_ws_core/src/main/native/include/wpi/halsim/ws_core/HALSimBaseWebSocketConnection.hpp b/simulation/halsim_ws_core/src/main/native/include/wpi/halsim/ws_core/HALSimBaseWebSocketConnection.hpp index 90930ec443..701ecfca27 100644 --- a/simulation/halsim_ws_core/src/main/native/include/wpi/halsim/ws_core/HALSimBaseWebSocketConnection.hpp +++ b/simulation/halsim_ws_core/src/main/native/include/wpi/halsim/ws_core/HALSimBaseWebSocketConnection.hpp @@ -6,7 +6,9 @@ #include -#include "wpi/util/json_fwd.hpp" +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpilibws { diff --git a/simulation/halsim_ws_core/src/main/native/include/wpi/halsim/ws_core/WSHalProviders.hpp b/simulation/halsim_ws_core/src/main/native/include/wpi/halsim/ws_core/WSHalProviders.hpp index 5c7d5f62b0..41ae362559 100644 --- a/simulation/halsim_ws_core/src/main/native/include/wpi/halsim/ws_core/WSHalProviders.hpp +++ b/simulation/halsim_ws_core/src/main/native/include/wpi/halsim/ws_core/WSHalProviders.hpp @@ -13,8 +13,10 @@ #include "wpi/hal/simulation/NotifyListener.h" #include "wpi/halsim/ws_core/WSBaseProvider.hpp" -#include "wpi/util/json_fwd.hpp" -#include "wpi/util/mutex.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpilibws { diff --git a/simulation/halsim_ws_server/src/main/native/cpp/HALSimHttpConnection.cpp b/simulation/halsim_ws_server/src/main/native/cpp/HALSimHttpConnection.cpp index a77e7119f5..2a942ae6e9 100644 --- a/simulation/halsim_ws_server/src/main/native/cpp/HALSimHttpConnection.cpp +++ b/simulation/halsim_ws_server/src/main/native/cpp/HALSimHttpConnection.cpp @@ -54,16 +54,12 @@ void HALSimHttpConnection::ProcessWsUpgrade() { return; } - wpi::util::json j; - try { - j = wpi::util::json::parse(msg); - } catch (const wpi::util::json::parse_error& e) { - std::string err("JSON parse failed: "); - err += e.what(); - m_websocket->Fail(400, err); + auto j = wpi::util::json::parse(msg); + if (!j) { + m_websocket->Fail(400, fmt::format("JSON parse failed: {}", j.error())); return; } - m_server->OnNetValueChanged(j); + m_server->OnNetValueChanged(*j); }); m_websocket->closed.connect([this](uint16_t, auto) { @@ -80,11 +76,11 @@ void HALSimHttpConnection::ProcessWsUpgrade() { void HALSimHttpConnection::OnSimValueChanged(const wpi::util::json& msg) { // Skip sending if this message is not in the allowed filter list try { - auto& type = msg.at("type").get_ref(); + auto& type = msg.at("type").get_string(); if (!m_server->CanSendMessage(type)) { return; } - } catch (wpi::util::json::exception& e) { + } catch (std::logic_error& e) { wpi::util::print(stderr, "Error with message: {}\n", e.what()); } diff --git a/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp b/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp index 035c70ed14..0e7af186be 100644 --- a/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp +++ b/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp @@ -162,8 +162,8 @@ void HALSimWeb::OnNetValueChanged(const wpi::util::json& msg) { // generate the key try { - auto& type = msg.at("type").get_ref(); - auto& device = msg.at("device").get_ref(); + auto& type = msg.at("type").get_string(); + auto& device = msg.at("device").get_string(); wpi::util::SmallString<64> key; key.append(type); @@ -176,7 +176,7 @@ void HALSimWeb::OnNetValueChanged(const wpi::util::json& msg) { if (provider) { provider->OnNetValueChanged(msg.at("data")); } - } catch (wpi::util::json::exception& e) { + } catch (std::logic_error& e) { wpi::util::print(stderr, "Error with incoming message: {}\n", e.what()); } } diff --git a/simulation/halsim_ws_server/src/main/native/include/wpi/halsim/ws_server/HALSimHttpConnection.hpp b/simulation/halsim_ws_server/src/main/native/include/wpi/halsim/ws_server/HALSimHttpConnection.hpp index 507909e47f..7bd72cc9d1 100644 --- a/simulation/halsim_ws_server/src/main/native/include/wpi/halsim/ws_server/HALSimHttpConnection.hpp +++ b/simulation/halsim_ws_server/src/main/native/include/wpi/halsim/ws_server/HALSimHttpConnection.hpp @@ -4,7 +4,6 @@ #pragma once -#include #include #include #include @@ -12,10 +11,11 @@ #include "wpi/halsim/ws_core/HALSimBaseWebSocketConnection.hpp" #include "wpi/halsim/ws_server/HALSimWeb.hpp" #include "wpi/net/HttpWebSocketServerConnection.hpp" -#include "wpi/net/uv/AsyncFunction.hpp" #include "wpi/net/uv/Buffer.hpp" -#include "wpi/util/json_fwd.hpp" -#include "wpi/util/mutex.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpilibws { diff --git a/simulation/halsim_ws_server/src/main/native/include/wpi/halsim/ws_server/HALSimWeb.hpp b/simulation/halsim_ws_server/src/main/native/include/wpi/halsim/ws_server/HALSimWeb.hpp index 0fa352424c..cd5b9ed160 100644 --- a/simulation/halsim_ws_server/src/main/native/include/wpi/halsim/ws_server/HALSimWeb.hpp +++ b/simulation/halsim_ws_server/src/main/native/include/wpi/halsim/ws_server/HALSimWeb.hpp @@ -16,7 +16,10 @@ #include "wpi/net/uv/Loop.hpp" #include "wpi/net/uv/Tcp.hpp" #include "wpi/util/StringMap.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpilibws { diff --git a/simulation/halsim_ws_server/src/test/native/cpp/WebServerClientTest.cpp b/simulation/halsim_ws_server/src/test/native/cpp/WebServerClientTest.cpp index 06bd4e43b3..bbf8e68ac7 100644 --- a/simulation/halsim_ws_server/src/test/native/cpp/WebServerClientTest.cpp +++ b/simulation/halsim_ws_server/src/test/native/cpp/WebServerClientTest.cpp @@ -44,18 +44,15 @@ void WebServerClientTest::InitializeWebSocket(const std::string& host, int port, }); m_websocket->text.connect([this](auto msg, bool) { - wpi::util::json j; - try { - j = wpi::util::json::parse(msg); - } catch (const wpi::util::json::parse_error& e) { - std::string err("JSON parse failed: "); - err += e.what(); + auto j = wpi::util::json::parse(msg); + if (!j) { + auto err = fmt::format("JSON parse failed: {}", j.error()); wpi::util::print(stderr, "{}\n", err); m_websocket->Fail(1003, err); return; } // Save last message received - m_json = j; + m_json = *j; }); m_websocket->closed.connect([this](uint16_t, auto) { diff --git a/simulation/halsim_ws_server/src/test/native/cpp/main.cpp b/simulation/halsim_ws_server/src/test/native/cpp/main.cpp index 9b35d23cc8..8c3544c857 100644 --- a/simulation/halsim_ws_server/src/test/native/cpp/main.cpp +++ b/simulation/halsim_ws_server/src/test/native/cpp/main.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -78,14 +79,13 @@ TEST_F(WebServerIntegrationTest, DISABLED_DigitalOutput) { bool test_value = true; // Default value from HAL initialization try { auto& msg = m_webserverClient->GetLastMessage(); - test_type = msg.at("type").get_ref(); - test_device = msg.at("device").get_ref(); + test_type = msg.at("type").get_string(); + test_device = msg.at("device").get_string(); auto& data = msg.at("data"); - wpi::util::json::const_iterator it = data.find("<>value"); - if (it != data.end()) { - test_value = it.value(); + if (auto val = data.lookup("<>value"); val && val->is_bool()) { + test_value = val->get_bool(); } - } catch (wpi::util::json::exception& e) { + } catch (std::logic_error& e) { wpi::util::print(stderr, "Error with incoming message: {}\n", e.what()); } @@ -109,10 +109,11 @@ TEST_F(WebServerIntegrationTest, DISABLED_DigitalInput) { return; } if (IsConnectedClientWS()) { - wpi::util::json msg = {{"type", "DIO"}, - {"device", std::to_string(PIN)}, - {"data", {{"<>value", EXPECTED_VALUE}}}}; - wpi::util::print("***** Input JSON: {}\n", msg.dump()); + wpi::util::json msg = wpi::util::json::object(); + msg["type"] = "DIO"; + msg["device"] = std::to_string(PIN); + msg["data"] = wpi::util::json::object("<>value", EXPECTED_VALUE); + wpi::util::print("***** Input JSON: {}\n", msg.to_string()); m_webserverClient->SendMessage(msg); done = true; } @@ -143,11 +144,14 @@ TEST_F(WebServerIntegrationTest, DriverStation) { return; } if (IsConnectedClientWS()) { - wpi::util::json msg = { - {"type", "DriverStation"}, - {"device", ""}, - {"data", {{">enabled", EXPECTED_VALUE}, {">new_data", true}}}}; - wpi::util::print("***** Input JSON: {}\n", msg.dump()); + auto data = wpi::util::json::object(); + data[">enabled"] = EXPECTED_VALUE; + data[">new_data"] = true; + wpi::util::json msg = wpi::util::json::object(); + msg["type"] = "DriverStation"; + msg["device"] = ""; + msg["data"] = std::move(data); + wpi::util::print("***** Input JSON: {}\n", msg.to_string()); m_webserverClient->SendMessage(msg); done = true; } diff --git a/simulation/halsim_xrp/src/main/native/cpp/HALSimXRP.cpp b/simulation/halsim_xrp/src/main/native/cpp/HALSimXRP.cpp index c2a30408eb..88422caf93 100644 --- a/simulation/halsim_xrp/src/main/native/cpp/HALSimXRP.cpp +++ b/simulation/halsim_xrp/src/main/native/cpp/HALSimXRP.cpp @@ -107,8 +107,8 @@ void HALSimXRP::ParsePacket(std::span packet) { void HALSimXRP::OnNetValueChanged(const wpi::util::json& msg) { try { - auto& type = msg.at("type").get_ref(); - auto& device = msg.at("device").get_ref(); + auto& type = msg.at("type").get_string(); + auto& device = msg.at("device").get_string(); wpi::util::SmallString<64> key; key.append(type); @@ -121,16 +121,17 @@ void HALSimXRP::OnNetValueChanged(const wpi::util::json& msg) { if (provider) { provider->OnNetValueChanged(msg.at("data")); } - } catch (wpi::util::json::exception& e) { + } catch (std::logic_error& e) { wpi::util::print(stderr, "Error with incoming message: {}\n", e.what()); } } void HALSimXRP::OnSimValueChanged(const wpi::util::json& simData) { // We'll use a signal from robot code to send all the data - if (simData["type"] == "HAL") { - auto halData = simData["data"]; - if (halData.find(">sim_periodic_after") != halData.end()) { + auto type = simData.lookup("type"); + if (type->is_string() && type->get_string() == "HAL") { + auto halData = simData.lookup("data"); + if (halData && halData->contains(">sim_periodic_after")) { SendStateToXRP(); } } else { diff --git a/simulation/halsim_xrp/src/main/native/cpp/XRP.cpp b/simulation/halsim_xrp/src/main/native/cpp/XRP.cpp index 5993a32dc0..9dbe119426 100644 --- a/simulation/halsim_xrp/src/main/native/cpp/XRP.cpp +++ b/simulation/halsim_xrp/src/main/native/cpp/XRP.cpp @@ -6,6 +6,7 @@ #include #include +#include #include @@ -33,21 +34,23 @@ XRP::XRP() } void XRP::HandleWPILibUpdate(const wpi::util::json& data) { - if (data.count("type") == 0) { + auto type = data.lookup("type"); + if (!type || !type->is_string()) { return; } - if (data["type"] == "DriverStation") { + auto& typeStr = type->get_string(); + if (typeStr == "DriverStation") { HandleDriverStationSimValueChanged(data); - } else if (data["type"] == "XRPMotor") { + } else if (typeStr == "XRPMotor") { HandleMotorSimValueChanged(data); - } else if (data["type"] == "XRPServo") { + } else if (typeStr == "XRPServo") { HandleServoSimValueChanged(data); - } else if (data["type"] == "DIO") { + } else if (typeStr == "DIO") { HandleDIOSimValueChanged(data); - } else if (data["type"] == "Gyro") { + } else if (typeStr == "Gyro") { HandleGyroSimValueChanged(data); - } else if (data["type"] == "Encoder") { + } else if (typeStr == "Encoder") { HandleEncoderSimValueChanged(data); } } @@ -108,56 +111,84 @@ void XRP::SetupXRPSendBuffer(wpi::net::raw_uv_ostream& buf) { // WPILib Sim Handlers void XRP::HandleDriverStationSimValueChanged(const wpi::util::json& data) { - auto dsData = data["data"]; - if (dsData.find(">enabled") != dsData.end()) { - m_robot_enabled = dsData[">enabled"]; + auto dsData = data.lookup("data"); + if (!dsData || !dsData->is_object()) { + return; + } + auto enabled = dsData->lookup(">enabled"); + if (enabled && enabled->is_bool()) { + m_robot_enabled = enabled->get_bool(); } } void XRP::HandleMotorSimValueChanged(const wpi::util::json& data) { int deviceId = -1; - auto motorData = data["data"]; + auto motorData = data.lookup("data"); + if (!motorData || !motorData->is_object()) { + return; + } - if (data["device"] == "motorL") { + auto device = data.lookup("device"); + if (!device || !device->is_string()) { + return; + } + + auto& deviceStr = device->get_string(); + if (deviceStr == "motorL") { deviceId = 0; - } else if (data["device"] == "motorR") { + } else if (deviceStr == "motorR") { deviceId = 1; - } else if (data["device"] == "motor3") { + } else if (deviceStr == "motor3") { deviceId = 2; - } else if (data["device"] == "motor4") { + } else if (deviceStr == "motor4") { deviceId = 3; } - if (deviceId != -1 && motorData.find("lookup("is_number()) { + m_motor_outputs[deviceId] = dutyCycle->get_number(); } } void XRP::HandleServoSimValueChanged(const wpi::util::json& data) { int deviceId = -1; - auto servoData = data["data"]; + auto servoData = data.lookup("data"); + if (!servoData || !servoData->is_object()) { + return; + } - if (data["device"] == "servo1") { + auto device = data.lookup("device"); + if (!device || !device->is_string()) { + return; + } + + auto& deviceStr = device->get_string(); + if (deviceStr == "servo1") { deviceId = 4; - } else if (data["device"] == "servo2") { + } else if (deviceStr == "servo2") { deviceId = 5; - } else if (data["device"] == "servo3") { + } else if (deviceStr == "servo3") { deviceId = 6; - } else if (data["device"] == "servo4") { + } else if (deviceStr == "servo4") { deviceId = 7; } - if (deviceId != -1 && servoData.find("lookup("is_number()) { + m_servo_outputs[deviceId] = position->get_number(); } } void XRP::HandleDIOSimValueChanged(const wpi::util::json& data) { int deviceId = -1; - auto dioData = data["data"]; + auto dioData = data.lookup("data"); + auto device = data.lookup("device"); + if (!device || !device->is_string()) { + return; + } try { - deviceId = std::stoi(data["device"].get()); + deviceId = std::stoi(device->get_string()); } catch (const std::invalid_argument&) { deviceId = -1; } @@ -167,26 +198,31 @@ void XRP::HandleDIOSimValueChanged(const wpi::util::json& data) { return; } - if (dioData.find("lookup("is_bool() && init->get_bool()) { // All DIOs are initialized as inputs by default m_digital_inputs.emplace(deviceId, false); } - if (dioData.find("lookup("is_bool() && !input->get_bool()) { // We're registering an output device // Remove from the digital inputs list (if present) m_digital_inputs.erase(deviceId); m_digital_outputs.emplace(deviceId, false); } - if (dioData.find("<>value") != dioData.end() && - m_digital_outputs.count(deviceId) > 0) { - m_digital_outputs[deviceId] = dioData["<>value"]; + auto value = dioData->lookup("<>value"); + if (value && value->is_bool() && m_digital_outputs.count(deviceId) > 0) { + m_digital_outputs[deviceId] = value->get_bool(); } } void XRP::HandleGyroSimValueChanged(const wpi::util::json& data) { - m_gyro_name = data["device"].get(); + auto name = data.lookup("device"); + if (name && name->is_string()) { + m_gyro_name = name->get_string(); + } } void XRP::HandleEncoderSimValueChanged(const wpi::util::json& data) { @@ -196,10 +232,18 @@ void XRP::HandleEncoderSimValueChanged(const wpi::util::json& data) { // 8/9 -> Encoder 2 // 10/11 -> Encoder 3 int deviceId = -1; - auto encData = data["data"]; + auto encData = data.lookup("data"); + if (!encData || !encData->is_object()) { + return; + } + + auto device = data.lookup("device"); + if (!device || !device->is_string()) { + return; + } try { - deviceId = std::stoi(data["device"].get()); + deviceId = std::stoi(device->get_string()); } catch (const std::invalid_argument&) { deviceId = -1; } @@ -208,10 +252,14 @@ void XRP::HandleEncoderSimValueChanged(const wpi::util::json& data) { return; } - if (encData.find("lookup("lookup("lookup("is_bool() && init->get_bool() && jchA && jchA->is_int() && + jchB && jchB->is_int()) { // The get_int(); + int chB = jchB->get_int(); if ((chA == 4 && chB == 5) || (chA == 5 && chB == 4)) { m_encoder_channel_map.emplace(0, deviceId); @@ -302,9 +350,14 @@ void XRP::ReadGyroTag(std::span packet) { wpi::util::json gyroJson; gyroJson["type"] = "Gyro"; gyroJson["device"] = m_gyro_name; - gyroJson["data"] = {{">rate_x", rate_x}, {">rate_y", rate_y}, - {">rate_z", rate_z}, {">angle_x", angle_x}, - {">angle_y", angle_y}, {">angle_z", angle_z}}; + auto data = wpi::util::json::object(); + data[">rate_x"] = rate_x; + data[">rate_y"] = rate_y; + data[">rate_z"] = rate_z; + data[">angle_x"] = angle_x; + data[">angle_y"] = angle_y; + data[">angle_z"] = angle_z; + gyroJson["data"] = std::move(data); // Update WPILib m_wpilib_update_func(gyroJson); @@ -318,7 +371,7 @@ void XRP::ReadDIOTag(std::span packet) { wpi::util::json dioJson; dioJson["type"] = "DIO"; dioJson["device"] = std::to_string(packet[2]); - dioJson["data"] = {{"<>value", packet[3] == 1}}; + dioJson["data"] = wpi::util::json::object("<>value", packet[3] == 1); m_wpilib_update_func(dioJson); } @@ -348,7 +401,7 @@ void XRP::ReadEncoderTag(std::span packet) { wpi::util::json encJson; encJson["type"] = "Encoder"; encJson["device"] = std::to_string(wpilibEncoderChannel); - encJson["data"] = {{">count", count}}; + encJson["data"] = wpi::util::json::object(">count", count); if (containsPeriod) { // Older versions of XRP firmware do not provide Encoder Period. @@ -375,8 +428,7 @@ void XRP::ReadEncoderTag(std::span packet) { period = -period; } - encJson["data"].push_back( - {wpi::util::json(">period"), wpi::util::json(period)}); + encJson["data"].emplace_back(wpi::util::json::object(">period", period)); } m_wpilib_update_func(encJson); @@ -396,7 +448,7 @@ void XRP::ReadAnalogTag(std::span packet) { wpi::util::json analogJson; analogJson["type"] = "AI"; analogJson["device"] = std::to_string(analogId); - analogJson["data"] = {{">voltage", voltage}}; + analogJson["data"] = wpi::util::json::object(">voltage", voltage); m_wpilib_update_func(analogJson); } diff --git a/simulation/halsim_xrp/src/main/native/include/wpi/halsim/xrp/HALSimXRP.hpp b/simulation/halsim_xrp/src/main/native/include/wpi/halsim/xrp/HALSimXRP.hpp index 2c234ac004..3badf135ec 100644 --- a/simulation/halsim_xrp/src/main/native/include/wpi/halsim/xrp/HALSimXRP.hpp +++ b/simulation/halsim_xrp/src/main/native/include/wpi/halsim/xrp/HALSimXRP.hpp @@ -18,7 +18,10 @@ #include "wpi/net/uv/Loop.hpp" #include "wpi/net/uv/Timer.hpp" #include "wpi/net/uv/Udp.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpilibxrp { diff --git a/simulation/halsim_xrp/src/main/native/include/wpi/halsim/xrp/XRP.hpp b/simulation/halsim_xrp/src/main/native/include/wpi/halsim/xrp/XRP.hpp index 2cb0af2e93..5d5b1a2bde 100644 --- a/simulation/halsim_xrp/src/main/native/include/wpi/halsim/xrp/XRP.hpp +++ b/simulation/halsim_xrp/src/main/native/include/wpi/halsim/xrp/XRP.hpp @@ -10,7 +10,6 @@ #include #include "wpi/net/raw_uv_ostream.hpp" -#include "wpi/util/json_fwd.hpp" #define XRP_TAG_MOTOR 0x12 #define XRP_TAG_SERVO 0x13 @@ -20,6 +19,10 @@ #define XRP_TAG_ACCEL 0x17 #define XRP_TAG_ENCODER 0x18 +namespace wpi::util { +class json; +} // namespace wpi::util + namespace wpilibxrp { using WPILibUpdateFunc = std::function; diff --git a/tools/sysid/src/main/native/cpp/view/Analyzer.cpp b/tools/sysid/src/main/native/cpp/view/Analyzer.cpp index a5d8e0fd59..afad133483 100644 --- a/tools/sysid/src/main/native/cpp/view/Analyzer.cpp +++ b/tools/sysid/src/main/native/cpp/view/Analyzer.cpp @@ -70,9 +70,6 @@ void Analyzer::UpdateFeedforwardGains() { } catch (const AnalysisManager::FileReadingError& e) { m_state = AnalyzerState::kFileError; HandleError(e.what()); - } catch (const wpi::util::json::exception& e) { - m_state = AnalyzerState::kFileError; - HandleError(e.what()); } catch (const std::exception& e) { m_state = AnalyzerState::kFileError; HandleError(e.what()); @@ -298,9 +295,6 @@ void Analyzer::PrepareData() { } catch (const AnalysisManager::FileReadingError& e) { m_state = AnalyzerState::kFileError; HandleError(e.what()); - } catch (const wpi::util::json::exception& e) { - m_state = AnalyzerState::kFileError; - HandleError(e.what()); } catch (const std::exception& e) { m_state = AnalyzerState::kFileError; HandleError(e.what()); diff --git a/tools/wpical/src/main/native/cpp/WPIcal.cpp b/tools/wpical/src/main/native/cpp/WPIcal.cpp index 78740c6238..b145f66fa8 100644 --- a/tools/wpical/src/main/native/cpp/WPIcal.cpp +++ b/tools/wpical/src/main/native/cpp/WPIcal.cpp @@ -3,7 +3,6 @@ // the WPILib BSD license file in the root directory of this project. #include -#include #include #include #include @@ -27,7 +26,10 @@ #include "wpi/gui/wpigui.hpp" #include "wpi/gui/wpigui_openurl.hpp" #include "wpi/math/util/MathUtil.hpp" +#include "wpi/util/MemoryBuffer.hpp" +#include "wpi/util/fs.hpp" #include "wpi/util/json.hpp" +#include "wpi/util/raw_ostream.hpp" namespace gui = wpi::gui; @@ -175,14 +177,26 @@ void CameraCalibrationSelectorButton(const char* text, if (selector && selector->ready(0)) { auto selectedFiles = selector->result(); if (!selectedFiles.empty()) { + auto fileBuffer = wpi::util::MemoryBuffer::GetFile(selectedFiles[0]); + if (!fileBuffer) { + goto err; + } + auto buffer = fileBuffer.value()->GetCharBuffer(); + auto j = wpi::util::json::parse({buffer.data(), buffer.size()}); + if (!j) { + goto err; + } try { - cameraModel = wpi::util::json::parse(std::ifstream(selectedFiles[0])) - .get(); + cameraModel = j->get(); } catch (...) { - ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_Always); - ImGui::OpenPopup("Camera Calibration Loading Error"); + goto err; } } + goto done; + err: + ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_Always); + ImGui::OpenPopup("Camera Calibration Loading Error"); + done: selector.reset(); } } @@ -199,15 +213,29 @@ void FieldSelectorButton(const char* text, auto selectedFiles = selector->result(); if (!selectedFiles.empty()) { std::string idealLayoutPath = selectedFiles[0]; + auto fileBuffer = wpi::util::MemoryBuffer::GetFile(idealLayoutPath); + if (!fileBuffer) { + gInvalidLayoutPath = idealLayoutPath; + goto err; + } + auto buffer = fileBuffer.value()->GetCharBuffer(); + auto j = wpi::util::json::parse({buffer.data(), buffer.size()}); + if (!j) { + gInvalidLayoutPath = idealLayoutPath; + goto err; + } try { - layout = wpi::util::json::parse(std::ifstream(idealLayoutPath)) - .get(); + layout = j->get(); } catch (...) { gInvalidLayoutPath = idealLayoutPath; - ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_Always); - ImGui::OpenPopup("AprilTag Field Layout Loading Error"); + goto err; } } + goto done; + err: + ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_Always); + ImGui::OpenPopup("AprilTag Field Layout Loading Error"); + done: selector.reset(); } if (layout.GetTags().size() > 0) { @@ -226,11 +254,18 @@ void SaveCalibratedField(const wpi::apriltag::AprilTagFieldLayout& field, static std::string saveDir; ProcessDirectorySelector(saveDirSelector, saveDir); if (!saveDir.empty()) { - std::ofstream out(saveDir + "/" + outputName + ".json"); - out << wpi::util::json{field}.dump(4); + std::error_code ec; + wpi::util::raw_fd_ostream out(saveDir + "/" + outputName + ".json", ec, + fs::OF_Text); + if (!ec) { + wpi::util::json{field}.marshal(out, true, 4); + } - std::ofstream fmap(saveDir + "/" + outputName + ".fmap"); - fmap << wpi::util::json{fmap::Fieldmap(field)}.dump(4); + wpi::util::raw_fd_ostream fmap(saveDir + "/" + outputName + ".fmap", ec, + fs::OF_Text); + if (!ec) { + wpi::util::json{fmap::Fieldmap(field)}.marshal(fmap, true, 4); + } saveDir.clear(); } @@ -286,8 +321,12 @@ void CalibrateCamera() { std::filesystem::path myPath(cameraVideoPath); auto outputPath = myPath.parent_path() / "cameracalibration.json"; - std::ofstream output_file(outputPath); - output_file << wpi::util::json(gCameraModel).dump(4) << std::endl; + std::error_code ec; + wpi::util::raw_fd_ostream output_file(outputPath.string(), ec, + fs::OF_Text); + if (!ec) { + wpi::util::json{gCameraModel}.marshal(output_file, true, 4); + } ImGui::CloseCurrentPopup(); calibrating = false; } else if (!videoProcessor->IsFinished()) { @@ -350,16 +389,30 @@ void CombineCalibrations() { if (!selectedFiles.empty()) { std::map fieldLayouts; for (auto& path : selectedFiles) { + auto fileBuffer = wpi::util::MemoryBuffer::GetFile(path); + if (!fileBuffer) { + gInvalidLayoutPath = path; + goto err; + } + auto buffer = fileBuffer.value()->GetCharBuffer(); + auto j = wpi::util::json::parse({buffer.data(), buffer.size()}); + if (!j) { + gInvalidLayoutPath = path; + goto err; + } try { - fieldLayouts.emplace( - path, wpi::util::json::parse(std::ifstream(path)) - .get()); + fieldLayouts.emplace(path, + j->get()); } catch (...) { gInvalidLayoutPath = path; - ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_Always); - ImGui::OpenPopup("AprilTag Field Layout Loading Error"); + goto err; } } + goto good; + err: + ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_Always); + ImGui::OpenPopup("AprilTag Field Layout Loading Error"); + good: calibratedFieldLayouts = fieldLayouts; } calibratedFieldLayoutMultiselector.reset(); diff --git a/tools/wpical/src/main/native/cpp/cameracalibration.cpp b/tools/wpical/src/main/native/cpp/cameracalibration.cpp index f753e1d6b2..3f7aceaa6e 100644 --- a/tools/wpical/src/main/native/cpp/cameracalibration.cpp +++ b/tools/wpical/src/main/native/cpp/cameracalibration.cpp @@ -306,47 +306,71 @@ void to_json(wpi::util::json& json, const CameraModel& cameraModel) { cameraModel.distortionCoefficients.data(), cameraModel.distortionCoefficients.data() + cameraModel.distortionCoefficients.size()); - json = {{"camera_matrix", cameraMatrix}, - {"distortion_coefficients", distortionCoefficients}, - {"avg_reprojection_error", cameraModel.avgReprojectionError}}; + json = wpi::util::json::object(); + json["camera_matrix"] = cameraMatrix; + json["distortion_coefficients"] = distortionCoefficients; + json["avg_reprojection_error"] = cameraModel.avgReprojectionError; } void from_json(const wpi::util::json& json, CameraModel& cameraModel) { bool isCalibdb = json.contains("camera"); Eigen::Matrix3d cameraMatrix; std::vector distortionCoeffs; - auto mat = json.at("camera_matrix"); - auto distortionCoefficients = json.at("distortion_coefficients"); + auto jmat = json.at("camera_matrix"); + auto jdistortionCoefficients = json.at("distortion_coefficients"); if (isCalibdb) { // OpenCV format has data key - if (mat.contains("data")) { - auto data = mat.at("data").get>(); + if (auto jdata = jmat.lookup("data")) { + auto& arr = jdata->get_array(); + std::vector data; + data.reserve(arr.size()); + for (const auto& item : arr) { + data.push_back(item.get_number()); + } cameraMatrix = Eigen::Matrix{data.data()}; } else { for (int i = 0; i < cameraMatrix.rows(); i++) { + auto& jcols = jmat.at(i); for (int j = 0; j < cameraMatrix.cols(); j++) { - cameraMatrix(i, j) = mat[i][j]; + cameraMatrix(i, j) = jcols.at(j).get_number(); } } } // OpenCV format has data key - if (distortionCoefficients.contains("data")) { - distortionCoeffs = - distortionCoefficients.at("data").get>(); + wpi::util::json::array_t* arr; + if (auto jdata = jdistortionCoefficients.lookup("data")) { + arr = &jdata->get_array(); } else { - distortionCoeffs = distortionCoefficients.get>(); + arr = &jdistortionCoefficients.get_array(); + } + distortionCoeffs.reserve(arr->size()); + for (const auto& item : *arr) { + distortionCoeffs.push_back(item.get_number()); } } else { - cameraMatrix = Eigen::Matrix{ - mat.get>().data()}; - distortionCoeffs = distortionCoefficients.get>(); + { + auto& arr = jmat.get_array(); + std::vector data; + data.reserve(arr.size()); + for (const auto& item : arr) { + data.push_back(item.get_number()); + } + cameraMatrix = Eigen::Matrix{data.data()}; + } + { + auto& arr = jdistortionCoefficients.get_array(); + distortionCoeffs.reserve(arr.size()); + for (const auto& item : arr) { + distortionCoeffs.push_back(item.get_number()); + } + } } // CalibDB generates JSONs with 5 values. Just zero out the remaining 3 to get // it to 8 distortionCoeffs.resize(8, 0); cameraModel = {cameraMatrix, Eigen::Matrix{distortionCoeffs.data()}, - json.at("avg_reprojection_error")}; + json.at("avg_reprojection_error").get_number()}; } } // namespace wpical diff --git a/tools/wpical/src/main/native/cpp/fmap.cpp b/tools/wpical/src/main/native/cpp/fmap.cpp index c584eb2cd2..181558d415 100644 --- a/tools/wpical/src/main/native/cpp/fmap.cpp +++ b/tools/wpical/src/main/native/cpp/fmap.cpp @@ -29,25 +29,39 @@ Fieldmap::Fieldmap(const wpi::apriltag::AprilTagFieldLayout& layout) } void fmap::to_json(wpi::util::json& json, const Fiducial& layout) { - json = {{"family", layout.family}, - {"id", layout.id}, - {"size", layout.size}, - {"transform", std::vector{layout.transform.data(), - layout.transform.data() + - layout.transform.size()}}, - {"unique", layout.unique}}; + json = wpi::util::json::object(); + json["family"] = layout.family; + json["id"] = layout.id; + json["size"] = layout.size; + json["transform"] = + std::vector{layout.transform.data(), + layout.transform.data() + layout.transform.size()}; + json["unique"] = layout.unique; } void fmap::from_json(const wpi::util::json& json, Fiducial& layout) { - auto vec = json.at("transform").get>(); - layout = {json.at("family"), json.at("id"), json.at("size"), - Eigen::Matrix4d{vec.data()}, json.at("unique")}; + auto& arr = json.at("transform").get_array(); + std::vector vec; + vec.reserve(arr.size()); + for (auto& elem : arr) { + vec.push_back(elem.get_number()); + } + layout = {json.at("family").get_string(), + static_cast(json.at("id").get_int()), + json.at("size").get_number(), Eigen::Matrix4d{vec.data()}, + static_cast(json.at("unique").get_int())}; } void fmap::to_json(wpi::util::json& json, const Fieldmap& layout) { - json = {{"type", "frc"}, {"fiducials", layout.fiducials}}; + json = wpi::util::json::object("type", "frc", "fiducials", layout.fiducials); } void fmap::from_json(const wpi::util::json& json, Fieldmap& layout) { - layout = {json.at("type"), json.at("fiducials")}; + auto& arr = json.at("fiducials").get_array(); + std::vector fiducials; + fiducials.reserve(arr.size()); + for (auto& elem : arr) { + fiducials.emplace_back(elem.get()); + } + layout = {json.at("type").get_string(), fiducials}; } diff --git a/tools/wpical/src/test/native/cpp/test_calibrate.cpp b/tools/wpical/src/test/native/cpp/test_calibrate.cpp index a5b582034c..fd7cf873da 100644 --- a/tools/wpical/src/test/native/cpp/test_calibrate.cpp +++ b/tools/wpical/src/test/native/cpp/test_calibrate.cpp @@ -2,7 +2,6 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include #include #include @@ -13,7 +12,10 @@ #include "path_lookup.hpp" #include "wpi/apriltag/AprilTagFieldLayout.hpp" #include "wpi/apriltag/AprilTagFields.hpp" +#include "wpi/util/MemoryBuffer.hpp" +#include "wpi/util/fs.hpp" #include "wpi/util/json.hpp" +#include "wpi/util/raw_ostream.hpp" const std::string projectRootPath = PROJECT_ROOT_PATH; @@ -42,8 +44,10 @@ TEST(CameraCalibrationTest, Typical) { } auto ret = calibrator.GetCameraModel(); EXPECT_NE(ret, std::nullopt); - std::ofstream output_file(calSavePath + "/cameracalibration.json"); - output_file << wpi::util::json(ret.value()).dump(4) << std::endl; + std::error_code ec; + wpi::util::raw_fd_ostream output_file(calSavePath + "/cameracalibration.json", + ec, fs::OF_Text); + wpi::util::json(ret.value()).marshal(output_file, true, 4); } TEST(CameraCalibrationTest, Atypical) { @@ -58,8 +62,10 @@ TEST(CameraCalibrationTest, Atypical) { } TEST(FieldCalibrationTest, Typical) { - auto model = wpi::util::json::parse( - std::ifstream(calSavePath + "/cameracalibration.json")) + auto buffer = + wpi::util::MemoryBuffer::GetFile(calSavePath + "/cameracalibration.json"); + auto buf = buffer.value()->GetCharBuffer(); + auto model = wpi::util::json::parse_or_throw({buf.data(), buf.size()}) .get(); auto ret = wpical::calibrate(LookupPath(projectRootPath + videoLocation), model, @@ -80,8 +86,10 @@ TEST(FieldCalibrationTest, Atypical_Bad_Camera_Model) { } TEST(FieldCalibrationTest, Atypical_Bad_Field_Layout) { - auto model = wpi::util::json::parse( - std::ifstream(calSavePath + "/cameracalibration.json")) + auto buffer = + wpi::util::MemoryBuffer::GetFile(calSavePath + "/cameracalibration.json"); + auto buf = buffer.value()->GetCharBuffer(); + auto model = wpi::util::json::parse_or_throw({buf.data(), buf.size()}) .get(); auto ret = wpical::calibrate(LookupPath(projectRootPath + videoLocation), model, @@ -90,8 +98,10 @@ TEST(FieldCalibrationTest, Atypical_Bad_Field_Layout) { } TEST(FieldCalibrationTest, Atypical_Bad_Input_Directory) { - auto model = wpi::util::json::parse( - std::ifstream(calSavePath + "/cameracalibration.json")) + auto buffer = + wpi::util::MemoryBuffer::GetFile(calSavePath + "/cameracalibration.json"); + auto buf = buffer.value()->GetCharBuffer(); + auto model = wpi::util::json::parse_or_throw({buf.data(), buf.size()}) .get(); auto ret = wpical::calibrate(LookupPath(projectRootPath), model, @@ -102,8 +112,10 @@ TEST(FieldCalibrationTest, Atypical_Bad_Input_Directory) { } TEST(FieldCalibrationTest, Atypical_Bad_Pinned_Tag) { - auto model = wpi::util::json::parse( - std::ifstream(calSavePath + "/cameracalibration.json")) + auto buffer = + wpi::util::MemoryBuffer::GetFile(calSavePath + "/cameracalibration.json"); + auto buf = buffer.value()->GetCharBuffer(); + auto model = wpi::util::json::parse_or_throw({buf.data(), buf.size()}) .get(); auto ret = wpical::calibrate(LookupPath(projectRootPath + videoLocation), model, @@ -114,8 +126,10 @@ TEST(FieldCalibrationTest, Atypical_Bad_Pinned_Tag) { } TEST(FieldCalibrationTest, Atypical_Bad_Pinned_Tag_Negative) { - auto model = wpi::util::json::parse( - std::ifstream(calSavePath + "/cameracalibration.json")) + auto buffer = + wpi::util::MemoryBuffer::GetFile(calSavePath + "/cameracalibration.json"); + auto buf = buffer.value()->GetCharBuffer(); + auto model = wpi::util::json::parse_or_throw({buf.data(), buf.size()}) .get(); auto ret = wpical::calibrate(LookupPath(projectRootPath + videoLocation), model, diff --git a/wpilibc/src/main/native/cpp/driverstation/DriverStation.cpp b/wpilibc/src/main/native/cpp/driverstation/DriverStation.cpp index 2e07237044..b1d22f4aea 100644 --- a/wpilibc/src/main/native/cpp/driverstation/DriverStation.cpp +++ b/wpilibc/src/main/native/cpp/driverstation/DriverStation.cpp @@ -87,10 +87,8 @@ struct MatchDataSender { std::shared_ptr table = wpi::nt::NetworkTableInstance::GetDefault().GetTable("FMSInfo"); MatchDataSenderEntry typeMetaData{ - table, - ".type", - kSmartDashboardType, - {{"SmartDashboard", kSmartDashboardType}}}; + table, ".type", kSmartDashboardType, + wpi::util::json::object("SmartDashboard", kSmartDashboardType)}; MatchDataSenderEntry gameData{table, "GameData", ""}; MatchDataSenderEntry eventName{table, "EventName", ""}; MatchDataSenderEntry matchNumber{table, "MatchNumber", diff --git a/wpilibc/src/main/native/cpp/smartdashboard/MechanismLigament2d.cpp b/wpilibc/src/main/native/cpp/smartdashboard/MechanismLigament2d.cpp index 62a34b1864..a1d150977a 100644 --- a/wpilibc/src/main/native/cpp/smartdashboard/MechanismLigament2d.cpp +++ b/wpilibc/src/main/native/cpp/smartdashboard/MechanismLigament2d.cpp @@ -29,7 +29,7 @@ void MechanismLigament2d::UpdateEntries( std::shared_ptr table) { m_typePub = table->GetStringTopic(".type").PublishEx( wpi::nt::StringTopic::TYPE_STRING, - {{"SmartDashboard", kSmartDashboardType}}); + wpi::util::json::object("SmartDashboard", kSmartDashboardType)); m_typePub.Set(kSmartDashboardType); m_colorEntry = table->GetStringTopic("color").GetEntry(""); diff --git a/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp b/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp index 808c4facb1..d619a07490 100644 --- a/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp +++ b/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp @@ -85,7 +85,8 @@ void SendableBuilderImpl::ClearProperties() { void SendableBuilderImpl::SetSmartDashboardType(std::string_view type) { if (!m_typePublisher) { m_typePublisher = m_table->GetStringTopic(".type").PublishEx( - wpi::nt::StringTopic::TYPE_STRING, {{"SmartDashboard", type}}); + wpi::nt::StringTopic::TYPE_STRING, + wpi::util::json::object("SmartDashboard", type)); } m_typePublisher.Set(type); } diff --git a/wpilibc/src/main/native/cpp/util/Preferences.cpp b/wpilibc/src/main/native/cpp/util/Preferences.cpp index f599a4a53b..87518937a9 100644 --- a/wpilibc/src/main/native/cpp/util/Preferences.cpp +++ b/wpilibc/src/main/native/cpp/util/Preferences.cpp @@ -32,7 +32,7 @@ struct Instance { wpi::nt::StringPublisher typePublisher{ table->GetStringTopic(".type").PublishEx( wpi::nt::StringTopic::TYPE_STRING, - {{"SmartDashboard", kSmartDashboardType}})}; + wpi::util::json::object("SmartDashboard", kSmartDashboardType))}; wpi::nt::MultiSubscriber tableSubscriber{ wpi::nt::NetworkTableInstance::GetDefault(), {{fmt::format("{}/", table->GetPath())}}}; diff --git a/wpimath/src/main/native/cpp/geometry/Pose2d.cpp b/wpimath/src/main/native/cpp/geometry/Pose2d.cpp index 7b05dfc51a..0c795203df 100644 --- a/wpimath/src/main/native/cpp/geometry/Pose2d.cpp +++ b/wpimath/src/main/native/cpp/geometry/Pose2d.cpp @@ -7,8 +7,9 @@ #include "wpi/util/json.hpp" void wpi::math::to_json(wpi::util::json& json, const Pose2d& pose) { - json = wpi::util::json{{"translation", pose.Translation()}, - {"rotation", pose.Rotation()}}; + json.set_object(); + json["translation"] = pose.Translation(); + json["rotation"] = pose.Rotation(); } void wpi::math::from_json(const wpi::util::json& json, Pose2d& pose) { diff --git a/wpimath/src/main/native/cpp/geometry/Pose3d.cpp b/wpimath/src/main/native/cpp/geometry/Pose3d.cpp index bac9f9f21c..c1838a6ebe 100644 --- a/wpimath/src/main/native/cpp/geometry/Pose3d.cpp +++ b/wpimath/src/main/native/cpp/geometry/Pose3d.cpp @@ -7,8 +7,9 @@ #include "wpi/util/json.hpp" void wpi::math::to_json(wpi::util::json& json, const Pose3d& pose) { - json = wpi::util::json{{"translation", pose.Translation()}, - {"rotation", pose.Rotation()}}; + json.set_object(); + json["translation"] = pose.Translation(); + json["rotation"] = pose.Rotation(); } void wpi::math::from_json(const wpi::util::json& json, Pose3d& pose) { diff --git a/wpimath/src/main/native/cpp/geometry/Quaternion.cpp b/wpimath/src/main/native/cpp/geometry/Quaternion.cpp index b2cecfe5dc..c2d4e1779b 100644 --- a/wpimath/src/main/native/cpp/geometry/Quaternion.cpp +++ b/wpimath/src/main/native/cpp/geometry/Quaternion.cpp @@ -7,14 +7,14 @@ #include "wpi/util/json.hpp" void wpi::math::to_json(wpi::util::json& json, const Quaternion& quaternion) { - json = wpi::util::json{{"W", quaternion.W()}, - {"X", quaternion.X()}, - {"Y", quaternion.Y()}, - {"Z", quaternion.Z()}}; + json.set_object(); + json["W"] = quaternion.W(); + json["X"] = quaternion.X(); + json["Y"] = quaternion.Y(); + json["Z"] = quaternion.Z(); } void wpi::math::from_json(const wpi::util::json& json, Quaternion& quaternion) { - quaternion = - Quaternion{json.at("W").get(), json.at("X").get(), - json.at("Y").get(), json.at("Z").get()}; + quaternion = Quaternion{json.at("W").get_number(), json.at("X").get_number(), + json.at("Y").get_number(), json.at("Z").get_number()}; } diff --git a/wpimath/src/main/native/cpp/geometry/Rotation2d.cpp b/wpimath/src/main/native/cpp/geometry/Rotation2d.cpp index 3c3b195def..04a551cac2 100644 --- a/wpimath/src/main/native/cpp/geometry/Rotation2d.cpp +++ b/wpimath/src/main/native/cpp/geometry/Rotation2d.cpp @@ -7,9 +7,9 @@ #include "wpi/util/json.hpp" void wpi::math::to_json(wpi::util::json& json, const Rotation2d& rotation) { - json = wpi::util::json{{"radians", rotation.Radians().value()}}; + json = wpi::util::json::object("radians", rotation.Radians().value()); } void wpi::math::from_json(const wpi::util::json& json, Rotation2d& rotation) { - rotation = Rotation2d{wpi::units::radian_t{json.at("radians").get()}}; + rotation = Rotation2d{wpi::units::radian_t{json.at("radians").get_number()}}; } diff --git a/wpimath/src/main/native/cpp/geometry/Rotation3d.cpp b/wpimath/src/main/native/cpp/geometry/Rotation3d.cpp index 5b81b54666..747374ba5b 100644 --- a/wpimath/src/main/native/cpp/geometry/Rotation3d.cpp +++ b/wpimath/src/main/native/cpp/geometry/Rotation3d.cpp @@ -7,7 +7,7 @@ #include "wpi/util/json.hpp" void wpi::math::to_json(wpi::util::json& json, const Rotation3d& rotation) { - json = wpi::util::json{{"quaternion", rotation.GetQuaternion()}}; + json = wpi::util::json::object("quaternion", rotation.GetQuaternion()); } void wpi::math::from_json(const wpi::util::json& json, Rotation3d& rotation) { diff --git a/wpimath/src/main/native/cpp/geometry/Translation2d.cpp b/wpimath/src/main/native/cpp/geometry/Translation2d.cpp index 633a823637..b7b7e5bf3e 100644 --- a/wpimath/src/main/native/cpp/geometry/Translation2d.cpp +++ b/wpimath/src/main/native/cpp/geometry/Translation2d.cpp @@ -8,12 +8,13 @@ void wpi::math::to_json(wpi::util::json& json, const Translation2d& translation) { - json = wpi::util::json{{"x", translation.X().value()}, - {"y", translation.Y().value()}}; + json.set_object(); + json["x"] = translation.X().value(); + json["y"] = translation.Y().value(); } void wpi::math::from_json(const wpi::util::json& json, Translation2d& translation) { - translation = Translation2d{wpi::units::meter_t{json.at("x").get()}, - wpi::units::meter_t{json.at("y").get()}}; + translation = Translation2d{wpi::units::meter_t{json.at("x").get_number()}, + wpi::units::meter_t{json.at("y").get_number()}}; } diff --git a/wpimath/src/main/native/cpp/geometry/Translation3d.cpp b/wpimath/src/main/native/cpp/geometry/Translation3d.cpp index 4b777e32b1..c82fdb961e 100644 --- a/wpimath/src/main/native/cpp/geometry/Translation3d.cpp +++ b/wpimath/src/main/native/cpp/geometry/Translation3d.cpp @@ -8,14 +8,15 @@ void wpi::math::to_json(wpi::util::json& json, const Translation3d& translation) { - json = wpi::util::json{{"x", translation.X().value()}, - {"y", translation.Y().value()}, - {"z", translation.Z().value()}}; + json.set_object(); + json["x"] = translation.X().value(); + json["y"] = translation.Y().value(); + json["z"] = translation.Z().value(); } void wpi::math::from_json(const wpi::util::json& json, Translation3d& translation) { - translation = Translation3d{wpi::units::meter_t{json.at("x").get()}, - wpi::units::meter_t{json.at("y").get()}, - wpi::units::meter_t{json.at("z").get()}}; + translation = Translation3d{wpi::units::meter_t{json.at("x").get_number()}, + wpi::units::meter_t{json.at("y").get_number()}, + wpi::units::meter_t{json.at("z").get_number()}}; } diff --git a/wpimath/src/main/native/cpp/trajectory/Trajectory.cpp b/wpimath/src/main/native/cpp/trajectory/Trajectory.cpp index d3c0336b72..9de19f7734 100644 --- a/wpimath/src/main/native/cpp/trajectory/Trajectory.cpp +++ b/wpimath/src/main/native/cpp/trajectory/Trajectory.cpp @@ -9,20 +9,21 @@ using namespace wpi::math; void wpi::math::to_json(wpi::util::json& json, const Trajectory::State& state) { - json = wpi::util::json{{"time", state.t.value()}, - {"velocity", state.velocity.value()}, - {"acceleration", state.acceleration.value()}, - {"pose", state.pose}, - {"curvature", state.curvature.value()}}; + json.set_object(); + json["time"] = state.t.value(); + json["velocity"] = state.velocity.value(); + json["acceleration"] = state.acceleration.value(); + json["pose"] = state.pose; + json["curvature"] = state.curvature.value(); } void wpi::math::from_json(const wpi::util::json& json, Trajectory::State& state) { state.pose = json.at("pose").get(); - state.t = wpi::units::second_t{json.at("time").get()}; + state.t = wpi::units::second_t{json.at("time").get_number()}; state.velocity = - wpi::units::meters_per_second_t{json.at("velocity").get()}; + wpi::units::meters_per_second_t{json.at("velocity").get_number()}; state.acceleration = wpi::units::meters_per_second_squared_t{ - json.at("acceleration").get()}; - state.curvature = wpi::units::curvature_t{json.at("curvature").get()}; + json.at("acceleration").get_number()}; + state.curvature = wpi::units::curvature_t{json.at("curvature").get_number()}; } diff --git a/wpimath/src/main/native/include/wpi/math/geometry/Pose2d.hpp b/wpimath/src/main/native/include/wpi/math/geometry/Pose2d.hpp index d716def254..b7a0c95919 100644 --- a/wpimath/src/main/native/include/wpi/math/geometry/Pose2d.hpp +++ b/wpimath/src/main/native/include/wpi/math/geometry/Pose2d.hpp @@ -15,7 +15,10 @@ #include "wpi/math/geometry/Translation2d.hpp" #include "wpi/units/length.hpp" #include "wpi/util/SymbolExports.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpi::math { diff --git a/wpimath/src/main/native/include/wpi/math/geometry/Pose3d.hpp b/wpimath/src/main/native/include/wpi/math/geometry/Pose3d.hpp index c9f3b034f7..9414e1ca5a 100644 --- a/wpimath/src/main/native/include/wpi/math/geometry/Pose3d.hpp +++ b/wpimath/src/main/native/include/wpi/math/geometry/Pose3d.hpp @@ -16,7 +16,10 @@ #include "wpi/math/geometry/Rotation3d.hpp" #include "wpi/math/geometry/Translation3d.hpp" #include "wpi/util/SymbolExports.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpi::math { diff --git a/wpimath/src/main/native/include/wpi/math/geometry/Quaternion.hpp b/wpimath/src/main/native/include/wpi/math/geometry/Quaternion.hpp index 14dcbae211..1e5980d8d8 100644 --- a/wpimath/src/main/native/include/wpi/math/geometry/Quaternion.hpp +++ b/wpimath/src/main/native/include/wpi/math/geometry/Quaternion.hpp @@ -10,7 +10,10 @@ #include #include "wpi/util/SymbolExports.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpi::math { diff --git a/wpimath/src/main/native/include/wpi/math/geometry/Rotation2d.hpp b/wpimath/src/main/native/include/wpi/math/geometry/Rotation2d.hpp index 3f64a2e14e..bdb2947e3a 100644 --- a/wpimath/src/main/native/include/wpi/math/geometry/Rotation2d.hpp +++ b/wpimath/src/main/native/include/wpi/math/geometry/Rotation2d.hpp @@ -15,7 +15,10 @@ #include "wpi/units/angle.hpp" #include "wpi/util/StackTrace.hpp" #include "wpi/util/SymbolExports.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpi::math { diff --git a/wpimath/src/main/native/include/wpi/math/geometry/Rotation3d.hpp b/wpimath/src/main/native/include/wpi/math/geometry/Rotation3d.hpp index 513182bf62..82200c8b82 100644 --- a/wpimath/src/main/native/include/wpi/math/geometry/Rotation3d.hpp +++ b/wpimath/src/main/native/include/wpi/math/geometry/Rotation3d.hpp @@ -17,7 +17,10 @@ #include "wpi/units/math.hpp" #include "wpi/util/MathExtras.hpp" #include "wpi/util/SymbolExports.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpi::math { diff --git a/wpimath/src/main/native/include/wpi/math/geometry/Translation2d.hpp b/wpimath/src/main/native/include/wpi/math/geometry/Translation2d.hpp index 1fd26d71a7..2c8d868c32 100644 --- a/wpimath/src/main/native/include/wpi/math/geometry/Translation2d.hpp +++ b/wpimath/src/main/native/include/wpi/math/geometry/Translation2d.hpp @@ -15,7 +15,10 @@ #include "wpi/units/length.hpp" #include "wpi/units/math.hpp" #include "wpi/util/SymbolExports.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpi::math { diff --git a/wpimath/src/main/native/include/wpi/math/geometry/Translation3d.hpp b/wpimath/src/main/native/include/wpi/math/geometry/Translation3d.hpp index b44cbf3b60..26039566ca 100644 --- a/wpimath/src/main/native/include/wpi/math/geometry/Translation3d.hpp +++ b/wpimath/src/main/native/include/wpi/math/geometry/Translation3d.hpp @@ -16,7 +16,10 @@ #include "wpi/units/length.hpp" #include "wpi/units/math.hpp" #include "wpi/util/SymbolExports.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpi::math { diff --git a/wpimath/src/main/native/include/wpi/math/trajectory/Trajectory.hpp b/wpimath/src/main/native/include/wpi/math/trajectory/Trajectory.hpp index e5cd3a6d49..8b65f0a957 100644 --- a/wpimath/src/main/native/include/wpi/math/trajectory/Trajectory.hpp +++ b/wpimath/src/main/native/include/wpi/math/trajectory/Trajectory.hpp @@ -17,7 +17,10 @@ #include "wpi/units/velocity.hpp" #include "wpi/util/MathExtras.hpp" #include "wpi/util/SymbolExports.hpp" -#include "wpi/util/json_fwd.hpp" + +namespace wpi::util { +class json; +} // namespace wpi::util namespace wpi::math { /** diff --git a/wpinet/src/main/native/cpp/DsClient.cpp b/wpinet/src/main/native/cpp/DsClient.cpp index 3b81175ae9..05b8a8c9be 100644 --- a/wpinet/src/main/native/cpp/DsClient.cpp +++ b/wpinet/src/main/native/cpp/DsClient.cpp @@ -94,8 +94,8 @@ void DsClient::ParseJson() { WPI_DEBUG4(m_logger, "DsClient JSON: {}", m_json); unsigned int ip = 0; try { - ip = wpi::util::json::parse(m_json).at("robotIP").get(); - } catch (wpi::util::json::exception& e) { + ip = wpi::util::json::parse_or_throw(m_json).at("robotIP").get_int(); + } catch (std::logic_error& e) { WPI_INFO(m_logger, "DsClient JSON error: {}", e.what()); return; } diff --git a/wpinet/src/main/native/cpp/WebServer.cpp b/wpinet/src/main/native/cpp/WebServer.cpp index 0a0df5bf10..db437ae693 100644 --- a/wpinet/src/main/native/cpp/WebServer.cpp +++ b/wpinet/src/main/native/cpp/WebServer.cpp @@ -279,17 +279,17 @@ void MyHttpConnection::ProcessRequest() { bool subdir = entry.is_directory(ec); std::string name = entry.path().filename().string(); if (subdir) { - dirs.emplace_back(wpi::util::json{{"name", std::move(name)}}); + dirs.emplace_back(wpi::util::json::object("name", std::move(name))); } else { files.emplace_back( - wpi::util::json{{"name", std::move(name)}, - {"size", subdir ? 0 : entry.file_size(ec)}}); + wpi::util::json::object("name", std::move(name), "size", + subdir ? 0 : entry.file_size(ec))); } } SendResponse(200, "OK", "text/json", - wpi::util::json{{"dirs", std::move(dirs)}, - {"files", std::move(files)}} - .dump()); + wpi::util::json::object("dirs", std::move(dirs), "files", + std::move(files)) + .to_string()); } else if (fs::exists(indexpath)) { SendFileResponse(200, "OK", GetMimeType("html"), indexpath, "Content-Disposition: filename=\"index.html\"\r\n"); diff --git a/wpiutil/src/main/python/wpiutil/src/type_casters/wpi_json_type_caster.h b/wpiutil/src/main/python/wpiutil/src/type_casters/wpi_json_type_caster.h index b10cddd3cc..c4e1086c1c 100644 --- a/wpiutil/src/main/python/wpiutil/src/type_casters/wpi_json_type_caster.h +++ b/wpiutil/src/main/python/wpiutil/src/type_casters/wpi_json_type_caster.h @@ -53,41 +53,46 @@ namespace pyjson { return py::none(); } - else if (j.is_boolean()) + else if (j.is_bool()) { - return py::bool_(j.get()); + return py::bool_(j.get_bool()); } - else if (j.is_number_unsigned()) + else if (j.is_int()) { - return py::int_(j.get()); + return py::int_(j.get_int()); } - else if (j.is_number_integer()) + else if (j.is_uint()) { - return py::int_(j.get()); + return py::int_(j.get_uint()); } - else if (j.is_number_float()) + else if (j.is_float()) { - return py::float_(j.get()); + return py::float_(j.get_float()); + } + else if (j.is_double()) + { + return py::float_(j.get_double()); } else if (j.is_string()) { - return py::str(j.get()); + return py::str(j.get_string()); } else if (j.is_array()) { - py::list obj(j.size()); - for (std::size_t i = 0; i < j.size(); i++) + auto& arr = j.get_array(); + py::list obj(arr.size()); + for (std::size_t i = 0; i < arr.size(); i++) { - obj[i] = from_json(j[i]); + obj[i] = from_json(arr[i]); } return std::move(obj); } else // Object { py::dict obj; - for (wpi::util::json::const_iterator it = j.cbegin(); it != j.cend(); ++it) + for (auto&& [key, value] : j.get_object()) { - obj[py::str(it.key())] = from_json(it.value()); + obj[py::str(key)] = from_json(value); } return std::move(obj); } @@ -147,7 +152,7 @@ namespace pyjson auto out = wpi::util::json::array(); for (const py::handle value : obj) { - out.push_back(to_json(value)); + out.emplace_back(to_json(value)); } return out; } @@ -173,55 +178,55 @@ namespace pyjson } } -// nlohmann_json serializers +// json serializers namespace wpi::util { - #define MAKE_NLJSON_SERIALIZER_DESERIALIZER(T) \ + #define MAKE_JSON_SERIALIZER_DESERIALIZER(T) \ template <> \ - struct adl_serializer \ + struct json_serializer \ { \ - inline static void to_json(json& j, const T& obj) \ + inline static void to(json& j, const T& obj) \ { \ j = pyjson::to_json(obj); \ } \ \ - inline static T from_json(const json& j) \ + inline static T from(const json& j) \ { \ return pyjson::from_json(j); \ } \ } - #define MAKE_NLJSON_SERIALIZER_ONLY(T) \ + #define MAKE_JSON_SERIALIZER_ONLY(T) \ template <> \ - struct adl_serializer \ + struct json_serializer \ { \ - inline static void to_json(json& j, const T& obj) \ + inline static void to(json& j, const T& obj) \ { \ j = pyjson::to_json(obj); \ } \ } - MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::object); + MAKE_JSON_SERIALIZER_DESERIALIZER(py::object); - MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::bool_); - MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::int_); - MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::float_); - MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::str); + MAKE_JSON_SERIALIZER_DESERIALIZER(py::bool_); + MAKE_JSON_SERIALIZER_DESERIALIZER(py::int_); + MAKE_JSON_SERIALIZER_DESERIALIZER(py::float_); + MAKE_JSON_SERIALIZER_DESERIALIZER(py::str); - MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::list); - MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::tuple); - MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::dict); + MAKE_JSON_SERIALIZER_DESERIALIZER(py::list); + MAKE_JSON_SERIALIZER_DESERIALIZER(py::tuple); + MAKE_JSON_SERIALIZER_DESERIALIZER(py::dict); - MAKE_NLJSON_SERIALIZER_ONLY(py::handle); - MAKE_NLJSON_SERIALIZER_ONLY(py::detail::item_accessor); - MAKE_NLJSON_SERIALIZER_ONLY(py::detail::list_accessor); - MAKE_NLJSON_SERIALIZER_ONLY(py::detail::tuple_accessor); - MAKE_NLJSON_SERIALIZER_ONLY(py::detail::sequence_accessor); - MAKE_NLJSON_SERIALIZER_ONLY(py::detail::str_attr_accessor); - MAKE_NLJSON_SERIALIZER_ONLY(py::detail::obj_attr_accessor); + MAKE_JSON_SERIALIZER_ONLY(py::handle); + MAKE_JSON_SERIALIZER_ONLY(py::detail::item_accessor); + MAKE_JSON_SERIALIZER_ONLY(py::detail::list_accessor); + MAKE_JSON_SERIALIZER_ONLY(py::detail::tuple_accessor); + MAKE_JSON_SERIALIZER_ONLY(py::detail::sequence_accessor); + MAKE_JSON_SERIALIZER_ONLY(py::detail::str_attr_accessor); + MAKE_JSON_SERIALIZER_ONLY(py::detail::obj_attr_accessor); - #undef MAKE_NLJSON_SERIALIZER - #undef MAKE_NLJSON_SERIALIZER_ONLY + #undef MAKE_JSON_SERIALIZER_DESERIALIZER + #undef MAKE_JSON_SERIALIZER_ONLY } // pybind11 caster diff --git a/wpiutil/src/test/native/include/wpi/util/TestPrinters.hpp b/wpiutil/src/test/native/include/wpi/util/TestPrinters.hpp index eefb5bb9b3..6f3cf95fa0 100644 --- a/wpiutil/src/test/native/include/wpi/util/TestPrinters.hpp +++ b/wpiutil/src/test/native/include/wpi/util/TestPrinters.hpp @@ -35,7 +35,7 @@ void PrintTo(std::span val, ::std::ostream* os) { } inline void PrintTo(const json& val, ::std::ostream* os) { - *os << val.dump(); + *os << val.to_string(); } } // namespace wpi::util