Update for jart/json.cpp change

This commit is contained in:
Peter Johnson
2026-03-29 15:38:18 -07:00
parent de3e211fdb
commit 9ca93fa190
120 changed files with 1240 additions and 1087 deletions

View File

@@ -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<const std::string&>();
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<const std::string&>();
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;
}

View File

@@ -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<const wpi::util::json::array_t&>();
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<int>();
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<double>();
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<int64_t>();
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<std::string>();
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<std::unique_ptr<Storage>>();
}
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<bool>());
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<double>());
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<int64_t>());
valuePtr->int64Array->push_back(jvalue.get_int());
} else if (valuePtr->type == Storage::Value::kDoubleArray) {
valuePtr->doubleArray->push_back(jvalue.get<int64_t>());
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<uint64_t>());
} else if (valuePtr->type == Storage::Value::kDoubleArray) {
valuePtr->doubleArray->push_back(jvalue.get<uint64_t>());
} 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<const std::string&>());
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<Storage>());
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<Value>();
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<bool>();
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<double>();
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<int64_t>();
valuePtr->int64Val = jvalue.get_int();
break;
case wpi::util::json::value_t::number_unsigned:
valuePtr->Reset(Value::kInt64);
valuePtr->int64Val = jvalue.get<uint64_t>();
break;
case wpi::util::json::value_t::string:
case wpi::util::json::Type::String:
valuePtr->Reset(Value::kString);
valuePtr->stringVal = jvalue.get_ref<const std::string&>();
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<std::unique_ptr<Storage>>(
jarr.emplace_back(v->ToJson());
}
// remove any trailing empty items
while (!jarr.empty() && jarr.back().empty()) {
jarr.get_ref<wpi::util::json::array_t&>().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;
}

View File

@@ -451,16 +451,14 @@ void FieldInfo::LoadImage() {
bool FieldInfo::LoadJson(std::span<const char> 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<const char> is, std::string_view filename) {
// image filename
std::string image;
try {
image = j.at("field-image").get<std::string>();
} 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<const char> is, std::string_view filename) {
// corners
int top, left, bottom, right;
try {
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 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<const char> is, std::string_view filename) {
float width;
float height;
try {
width = j.at("field-size").at(0).get<float>();
height = j.at("field-size").at(1).get<float>();
} 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<const char> is, std::string_view filename) {
// units for size
std::string unit;
try {
unit = j.at("field-unit").get<std::string>();
} 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;

View File

@@ -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 {

View File

@@ -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))

View File

@@ -8,7 +8,6 @@
#include <cinttypes>
#include <cstring>
#include <functional>
#include <initializer_list>
#include <map>
#include <memory>
#include <span>
@@ -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<const bool*>()) {
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<const bool*>()) {
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<const std::string&>(), 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<bool>(), 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<int64_t>(), 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<uint64_t>(), time);
out->UpdateFromValue(model, name, "");
break;
case wpi::util::json::value_t::number_float:
out->value = wpi::nt::Value::MakeDouble(j.get<double>(), 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();