this post was submitted on 24 Jun 2025
11 points (100.0% liked)

C++

2040 readers
2 users here now

The center for all discussion and news regarding C++.

Rules

founded 2 years ago
MODERATORS
 

It's not fully finished yet, but it's getting there, and i didn't write documentation beyond the README.md and tests/test.cpp but I'd like some feedback on it.

features

  • It's a header only library that's currently < 3000 loc
  • no 3rd-party dependencies
  • support for being imported as a module
  • supports inserting std containers into json nodes
  • highly type safe, which is made possible by using concepts
  • easy-to-use object/array iterations
  • easy-to-use type casting from json value to native c++ types which is enabled by std::variant and concepts
  • exception-free parsing and value casting.
  • modern error handling using "expected" type
  • ! exception-free node.try_at("key") access is still not implemented but planned
  • and more
top 3 comments
sorted by: hot top controversial new old
[–] underscore_@sopuli.xyz 3 points 1 week ago (1 children)

Neat! Could you expand on why someone might prefer to use this instead of other json parsing libs such as nlohmann or simdjson?

[–] nodeluna@programming.dev 2 points 1 week ago* (last edited 1 week ago)

thank you! if someone wants a more modern API that's kinda similar to tomlplusplus and a little nicer to use with modern error handling then my library might come in handy. my API is inspired a lot by tomlplusplus . i was trying to make a build system that uses TOML as a config file and I needed a json library so i decided to make my own as a learning experience which was great.

I'm not familiar with simdjson, but i know a little about nlohmann and I think the exception free path using ljson::expected is a nicer/safer approach. also there is convenient operator overloads in my library to add objects/array together, but nlohmann also has that i think

// accessing values in ljson
ljson::node node = ljson::parser::parse(raw_json);
std::string val = node.at("key").as_string();

// accessing values in nlohmann
nlohmann::json::json json;
raw_json >> json;
std::string val = json["key"].get<std::string>();

[–] nodeluna@programming.dev 1 points 1 week ago* (last edited 1 week ago)

** AI GENERATED SHOWCASE THAT'S REVIEWED BY ME **

Here are some cool and advanced features of the ljson library, with short code snippets for each:

  1. Seamless Construction from C++ Containers

You can build JSON objects and arrays directly from standard containers (e.g., std::map, std::vector, std::set, etc.): C++

std::map<std::string, int> obj = {{"a", 1}, {"b", 2}};
std::vector<std::string> arr = {"x", "y", "z"};
ljson::node data;
data.insert("object", obj);
data.insert("array", arr);
  1. Initializer-List Magic (Python/JavaScript-like Syntax)
ljson::node n = {
    {"name", "Alice"},
    {"age", 30},
    {"active", true},
    {"tags", ljson::node({"dev", "cat_lover"})},
    {"profile", ljson::node({{"city", "Paris"}, {"zip", 75000}})}
};
// n is now a JSON object with nested objects and arrays!
  1. Type-Safe Value Accessors and Type Queries
if (n.at("age").is_integer())
    std::cout << "Age: " << n.at("age").as_integer() << "\n";
if (n.at("tags").is_array()) {
    for (auto& tag : *n.at("tags").as_array())
        std::cout << tag.as_string() << " ";
}
  1. Type-Safe Mutation and Assignment
n.at("name") = "Bob";      // changes value to "Bob"
n.at("age")  = 31;         // changes value to 31
n.at("active") = false;    // changes value to false
n.at("tags").push_back("gamer"); // add "gamer" to tags array
  1. Exception-Free Parsing (Error Handling Without throw)
auto result = ljson::parser::try_parse(R"({"x":1})");
if (result) {
    std::cout << "Parsed!\n";
} else {
    std::cerr << "Parse error: " << result.error().message() << "\n";
}
  1. Pretty Printing and File Output with Custom Indentation
n.dump_to_stdout({'\t', 2});         // Pretty print using tabs, 2 per indent
n.write_to_file("output.json");       // Write to file
std::string s = n.dump_to_string();   // Get pretty JSON string
  1. Operator Overloading for JSON Merge and Addition

Concatenate arrays and objects in a natural way:

ljson::node a = {1, 2, 3};
ljson::node b = {4, 5};
ljson::node c = a + b; // [1,2,3,4,5]

ljson::node obj1 = {{"x", 1}};
ljson::node obj2 = {{"y", 2}};
ljson::node obj3 = obj1 + obj2; // {"x":1,"y":2}
  1. Automatic Null Support
n.insert("nothing", ljson::null);
if (n.at("nothing").is_null())
    std::cout << "It's " << n.at("nothing").stringify() << "!\n"; // It's null!
  1. Direct Construction from Nested Initializer Lists
ljson::node arr = { 1, 2, 3, ljson::node({"nested", "array"}), ljson::null };
ljson::node obj = { {"a", 1}, {"b", ljson::node({2, 3, 4})}, {"c", ljson::node({"d", 5})} };
  1. Safe and Direct Value Setting and Mutation

You can set a node's value using .set() or assignment:

n.at("val").set(123.45);
n.at("flag") = true;
n.at("sub").insert("newkey", "newval");
  1. Full Traversal and Iteration Support
// Iterating an array
for (auto& item : *n.at("tags").as_array())
    std::cout << item.as_string() << "\n";

// Iterating an object
for (auto& [key, value] : *n.as_object())
    std::cout << key << ": " << value.stringify() << "\n";
  1. Type-Checked Try-Cast APIs

Get error info if you try an invalid conversion:

auto res = n.at("name").try_as_integer();
if (!res) std::cerr << "Not an integer: " << res.error().message() << "\n";
  1. Flexible Construction from Arbitrary Types

Any supported type (string, int, bool, null, etc.) or nested containers can be used directly in construction or insertion.

  1. Custom Indentation Everywhere
n.dump_to_stdout({' ', 8}); // 8 spaces per indent
  1. Chaining Insertions and Additions
ljson::node obj = {
    {"a", 1},
    {"b", 2}
};
obj += ljson::object_pairs{
    {"c", 3},
    {"d", 4}
};

Summary: ljson offers a modern, expressive, and type-safe C++ JSON API with C++ types, safety, and STL integration.