Skip to content

Instantly share code, notes, and snippets.

@dcousens
Last active September 29, 2022 15:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dcousens/800cb1d367d203edf37d4198d24800b5 to your computer and use it in GitHub Desktop.
Save dcousens/800cb1d367d203edf37d4198d24800b5 to your computer and use it in GitHub Desktop.
Naive JSON string serialisation
.test.actual
test
{"apples":23,"bananas":-54,"pears":22,"other":{"cocoa":15.54},"lettuce":null,"celery":"yes","vegetables":"probably","icecream":"quite likely","empty":false,"array":[1,2,3,281474976710655,9007199254740996,9007199254740996,9007199254740996]}
{"apples":23,"bananas":-54,"pears":22,"other":{"cocoa":15.54},"lettuce":null,"vegetables":"probably","icecream":"quite likely","empty":false,"array":[1,2,3]}
// MIT License
//
// Copyright (c) 2022 Daniel Cousens
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <array>
#include <cstdio> // snprintf
#include <cmath>
#include <initializer_list>
#include <string>
namespace json {
struct raw_t {
std::string const value;
};
struct value_t {
std::string value;
value_t () {}
value_t (raw_t const x): value(x.value) {}
value_t (std::string const x) {
auto output = std::string("\"");
output.append(x);
output.push_back('"');
value = std::move(output);
}
value_t (char const* x) {
auto output = std::string("\"");
output.append(x);
output.push_back('"');
value = std::move(output);
}
value_t (bool const x) : value(x ? "true" : "false") {}
value_t (uint8_t const x) : value(std::to_string(x)) {}
value_t (uint16_t const x) : value(std::to_string(x)) {}
value_t (uint32_t const x) : value(std::to_string(x)) {}
value_t (int8_t const x) : value(std::to_string(x)) {}
value_t (int16_t const x) : value(std::to_string(x)) {}
value_t (int32_t const x) : value(std::to_string(x)) {}
};
struct pair_t {
std::string const left;
value_t const right;
};
auto const undefined = raw_t{ "undefined" };
auto const null = raw_t{ "null" };
inline bool operator== (value_t const& a, value_t const& b) {
return a.value == b.value;
}
inline bool operator== (pair_t const& a, pair_t const& b) {
return a.left == b.left and a.right == b.right;
}
inline std::string stringify (value_t const& x) {
return x.value;
}
template <typename R = std::initializer_list<value_t>>
inline value_t array (R const values) {
auto output = std::string("[");
auto first = true;
for (auto const& x : values) {
if (not first) output.push_back(',');
first = false;
output.append(stringify(x));
}
output.push_back(']');
return raw_t{ std::move(output) };
}
template <typename R = std::initializer_list<pair_t>>
inline value_t object (R const pairs) {
auto output = std::string("{");
auto first = true;
for (auto const& pair : pairs) {
if (pair.right == undefined) continue;
if (not first) output.push_back(',');
first = false;
output.push_back('"');
output.append(pair.left);
output.push_back('"');
output.push_back(':');
output.append(stringify(pair.right));
}
output.push_back('}');
return raw_t{ std::move(output) };
}
inline value_t boolean (bool const x) { return value_t(x); }
inline value_t number (uint8_t const x) { return value_t(x); }
inline value_t number (uint16_t const x) { return value_t(x); }
inline value_t number (uint32_t const x) { return value_t(x); }
inline value_t number (int8_t const x) { return value_t(x); }
inline value_t number (int16_t const x) { return value_t(x); }
inline value_t number (int32_t const x) { return value_t(x); }
inline value_t string (const char* const x) { return value_t(x); }
inline value_t string (std::string const x) { return value_t(x); }
template <size_t N>
inline value_t number (double const x) {
if (not std::isfinite(x)) return null;
if (N == 0) return raw_t{ std::to_string(int64_t(x)) };
static_assert(N <= 4, "Unsupported");
auto b = std::array<char, 14 + N>{};
auto const w = snprintf(b.data(), b.size(),
N == 1 ? "%.1f" :
N == 2 ? "%.2f" :
N == 3 ? "%.3f" :
"%.4f", x);
if (w < 0 or w >= sizeof(b)) return null;
return raw_t{ std::string(b.data(), b.data() + w) };
}
inline pair_t pair (pair_t const& x) {
return x;
}
}
.PHONY: run-test
run-test: test
./test | jq
./test > .test.actual
cmp .test.actual .test.expected
test: test.cpp json.hpp
clang++ test.cpp -o test
#include <iostream>
#include <array>
#include <vector>
#include "json.hpp"
int main () {
std::cout << json::stringify(json::object({
{ "apples", 23 },
{ "bananas", -54 },
{ "pears", json::number<0>(22.064) },
{ "other", json::object({
{ "cacao", true ? json::undefined : json::number(10) },
{ "cocoa", json::number<2>(15.543) },
}) },
{ "lettuce", json::null },
{ "celery", json::raw_t{ "\"yes\"" } },
{ "vegetables", "probably" },
{ "icecream", json::string("quite likely") },
// { "when", json::string(__DATE__ ", " __TIME__) },
{ "empty", false },
{ "array", json::array({
1u,
2,
json::number<0>(3lu),
json::number(4),
json::number<0>(281474976710655),
json::number<0>(9007199254740995), // rounds to ...996
json::number<0>(9007199254740995.0), // rounds to ...996
json::number<0>(9007199254740997), // rounds to ...996
}) },
})) << std::endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment