Skip to content

Commit

Permalink
Add support of multi-dim C-style array member of struct.
Browse files Browse the repository at this point in the history
* Support up to 4 dimensional array.

* Modify pretty-print logic to print multi-dim array's inner most dimension in the same line for better readability.
  • Loading branch information
peng-wang-cn committed Jan 6, 2024
1 parent a259ecc commit dd0625f
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 12 deletions.
48 changes: 48 additions & 0 deletions include/nlohmann/detail/conversions/from_json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,54 @@ auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines
}
}

template<typename BasicJsonType, typename T, std::size_t N1, std::size_t N2>
auto from_json(const BasicJsonType& j, T (&arr)[N1][N2]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
-> decltype(j.template get<T>(), void())
{
for (std::size_t i1 = 0; i1 < N1; ++i1)
{
for (std::size_t i2 = 0; i2 < N2; ++i2)
{
arr[i1][i2] = j.at(i1).at(i2).template get<T>();
}
}
}

template<typename BasicJsonType, typename T, std::size_t N1, std::size_t N2, std::size_t N3>
auto from_json(const BasicJsonType& j, T (&arr)[N1][N2][N3]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
-> decltype(j.template get<T>(), void())
{
for (std::size_t i1 = 0; i1 < N1; ++i1)
{
for (std::size_t i2 = 0; i2 < N2; ++i2)
{
for (std::size_t i3 = 0; i3 < N3; ++i3)
{
arr[i1][i2][i3] = j.at(i1).at(i2).at(i3).template get<T>();
}
}
}
}

template<typename BasicJsonType, typename T, std::size_t N1, std::size_t N2, std::size_t N3, std::size_t N4>
auto from_json(const BasicJsonType& j, T (&arr)[N1][N2][N3][N4]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
-> decltype(j.template get<T>(), void())
{
for (std::size_t i1 = 0; i1 < N1; ++i1)
{
for (std::size_t i2 = 0; i2 < N2; ++i2)
{
for (std::size_t i3 = 0; i3 < N3; ++i3)
{
for (std::size_t i4 = 0; i4 < N4; ++i4)
{
arr[i1][i2][i3][i4] = j.at(i1).at(i2).at(i3).at(i4).template get<T>();
}
}
}
}
}

template<typename BasicJsonType>
inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
{
Expand Down
5 changes: 4 additions & 1 deletion include/nlohmann/detail/output/serializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,10 @@ class serializer
return;
}

if (pretty_print)
// For multi-dim number arrays, print the inner most dimension in one line to improve readability.
value_t elem_type = val.m_data.m_value.array->cbegin()->m_data.m_type;
bool elem_is_number = value_t::number_float == elem_type || value_t::number_integer == elem_type || value_t::number_unsigned == elem_type;
if (pretty_print && (!elem_is_number || value_t::array == elem_type))
{
o->write_characters("[\n", 2);

Expand Down
53 changes: 52 additions & 1 deletion single_include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4817,6 +4817,54 @@ auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines
}
}

template<typename BasicJsonType, typename T, std::size_t N1, std::size_t N2>
auto from_json(const BasicJsonType& j, T (&arr)[N1][N2]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
-> decltype(j.template get<T>(), void())
{
for (std::size_t i1 = 0; i1 < N1; ++i1)
{
for (std::size_t i2 = 0; i2 < N2; ++i2)
{
arr[i1][i2] = j.at(i1).at(i2).template get<T>();
}
}
}

template<typename BasicJsonType, typename T, std::size_t N1, std::size_t N2, std::size_t N3>
auto from_json(const BasicJsonType& j, T (&arr)[N1][N2][N3]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
-> decltype(j.template get<T>(), void())
{
for (std::size_t i1 = 0; i1 < N1; ++i1)
{
for (std::size_t i2 = 0; i2 < N2; ++i2)
{
for (std::size_t i3 = 0; i3 < N3; ++i3)
{
arr[i1][i2][i3] = j.at(i1).at(i2).at(i3).template get<T>();
}
}
}
}

template<typename BasicJsonType, typename T, std::size_t N1, std::size_t N2, std::size_t N3, std::size_t N4>
auto from_json(const BasicJsonType& j, T (&arr)[N1][N2][N3][N4]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
-> decltype(j.template get<T>(), void())
{
for (std::size_t i1 = 0; i1 < N1; ++i1)
{
for (std::size_t i2 = 0; i2 < N2; ++i2)
{
for (std::size_t i3 = 0; i3 < N3; ++i3)
{
for (std::size_t i4 = 0; i4 < N4; ++i4)
{
arr[i1][i2][i3][i4] = j.at(i1).at(i2).at(i3).at(i4).template get<T>();
}
}
}
}
}

template<typename BasicJsonType>
inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
{
Expand Down Expand Up @@ -18202,7 +18250,10 @@ class serializer
return;
}

if (pretty_print)
// For multi-dim number arrays, print the inner most dimension in one line to improve readability.
value_t elem_type = val.m_data.m_value.array->cbegin()->m_data.m_type;
bool elem_is_number = value_t::number_float == elem_type || value_t::number_integer == elem_type || value_t::number_unsigned == elem_type;
if (pretty_print && (!elem_is_number || value_t::array == elem_type))
{
o->write_characters("[\n", 2);

Expand Down
58 changes: 58 additions & 0 deletions tests/src/unit-conversions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,64 @@ TEST_CASE("value conversion")
CHECK(std::equal(std::begin(nbs), std::end(nbs), std::begin(nbs2)));
}

SECTION("built-in arrays: 2D")
{
const int nbs[][3] = {{0, 1, 2}, {3, 4, 5}}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
int nbs2[][3] = {{0, 0, 0}, {0, 0, 0}}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)

const json j2 = nbs;
j2.get_to(nbs2);
CHECK(std::equal(std::begin(nbs[0]), std::end(nbs[1]), std::begin(nbs2[0])));
}

SECTION("built-in arrays: 3D")
{
const int nbs[][2][3] = {\
{{0, 1, 2}, {3, 4, 5}}, \
{{10, 11, 12}, {13, 14, 15}}\
}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
int nbs2[][2][3] = {\
{{0, 0, 0}, {0, 0, 0}}, \
{{0, 0, 0}, {0, 0, 0}}\
}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)

const json j2 = nbs;
j2.get_to(nbs2);
CHECK(std::equal(std::begin(nbs[0][0]), std::end(nbs[1][1]), std::begin(nbs2[0][0])));
}

SECTION("built-in arrays: 4D")
{
const int nbs[][2][2][3] = {\
{
\
{{0, 1, 2}, {3, 4, 5}}, \
{{10, 11, 12}, {13, 14, 15}}\
}, \
{
\
{{20, 21, 22}, {23, 24, 25}}, \
{{30, 31, 32}, {33, 34, 35}}\
}\
}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
int nbs2[][2][2][3] = {\
{
\
{{0, 0, 0}, {0, 0, 0}}, \
{{0, 0, 0}, {0, 0, 0}}\
}, \
{
\
{{0, 0, 0}, {0, 0, 0}}, \
{{0, 0, 0}, {0, 0, 0}}\
}\
}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)

const json j2 = nbs;
j2.get_to(nbs2);
CHECK(std::equal(std::begin(nbs[0][0][0]), std::end(nbs[1][1][1]), std::begin(nbs2[0][0][0])));
}

SECTION("std::deque<json>")
{
std::deque<json> a{"previous", "value"};
Expand Down
31 changes: 21 additions & 10 deletions tests/src/unit-inspection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,44 +202,55 @@ TEST_CASE("object inspection")

SECTION("serialization")
{
json const j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} };
json const j
{
{"object", json::object()},
{"array", {1, 2, 3, 4}},
{"array2d", {{1, 2, 3}, {4, 5, 6}}},
{"array3d", {{{1, 2, 3}, {4, 5, 6}}, {{2, 3, 4}, {5, 6, 7}}}},
{"array4d", {{{{1, 2, 3}, {4, 5, 6}}, {{2, 3, 4}, {5, 6, 7}}}, {{{11, 12, 13}, {14, 15, 16}}, {{12, 13, 14}, {15, 16, 17}}}}},
{"number", 42},
{"boolean", false},
{"null", nullptr},
{"string", "Hello world"}
};

SECTION("no indent / indent=-1")
{
CHECK(j.dump() ==
"{\"array\":[1,2,3,4],\"boolean\":false,\"null\":null,\"number\":42,\"object\":{},\"string\":\"Hello world\"}");
"{\"array\":[1,2,3,4],\"array2d\":[[1,2,3],[4,5,6]],\"array3d\":[[[1,2,3],[4,5,6]],[[2,3,4],[5,6,7]]],\"array4d\":[[[[1,2,3],[4,5,6]],[[2,3,4],[5,6,7]]],[[[11,12,13],[14,15,16]],[[12,13,14],[15,16,17]]]],\"boolean\":false,\"null\":null,\"number\":42,\"object\":{},\"string\":\"Hello world\"}");

CHECK(j.dump() == j.dump(-1));
}

SECTION("indent=0")
{
CHECK(j.dump(0) ==
"{\n\"array\": [\n1,\n2,\n3,\n4\n],\n\"boolean\": false,\n\"null\": null,\n\"number\": 42,\n\"object\": {},\n\"string\": \"Hello world\"\n}");
"{\n\"array\": [1,2,3,4],\n\"array2d\": [\n[1,2,3],\n[4,5,6]\n],\n\"array3d\": [\n[\n[1,2,3],\n[4,5,6]\n],\n[\n[2,3,4],\n[5,6,7]\n]\n],\n\"array4d\": [\n[\n[\n[1,2,3],\n[4,5,6]\n],\n[\n[2,3,4],\n[5,6,7]\n]\n],\n[\n[\n[11,12,13],\n[14,15,16]\n],\n[\n[12,13,14],\n[15,16,17]\n]\n]\n],\n\"boolean\": false,\n\"null\": null,\n\"number\": 42,\n\"object\": {},\n\"string\": \"Hello world\"\n}");
}

SECTION("indent=1, space='\t'")
{
CHECK(j.dump(1, '\t') ==
"{\n\t\"array\": [\n\t\t1,\n\t\t2,\n\t\t3,\n\t\t4\n\t],\n\t\"boolean\": false,\n\t\"null\": null,\n\t\"number\": 42,\n\t\"object\": {},\n\t\"string\": \"Hello world\"\n}");
"{\n\t\"array\": [1,2,3,4],\n\t\"array2d\": [\n\t\t[1,2,3],\n\t\t[4,5,6]\n\t],\n\t\"array3d\": [\n\t\t[\n\t\t\t[1,2,3],\n\t\t\t[4,5,6]\n\t\t],\n\t\t[\n\t\t\t[2,3,4],\n\t\t\t[5,6,7]\n\t\t]\n\t],\n\t\"array4d\": [\n\t\t[\n\t\t\t[\n\t\t\t\t[1,2,3],\n\t\t\t\t[4,5,6]\n\t\t\t],\n\t\t\t[\n\t\t\t\t[2,3,4],\n\t\t\t\t[5,6,7]\n\t\t\t]\n\t\t],\n\t\t[\n\t\t\t[\n\t\t\t\t[11,12,13],\n\t\t\t\t[14,15,16]\n\t\t\t],\n\t\t\t[\n\t\t\t\t[12,13,14],\n\t\t\t\t[15,16,17]\n\t\t\t]\n\t\t]\n\t],\n\t\"boolean\": false,\n\t\"null\": null,\n\t\"number\": 42,\n\t\"object\": {},\n\t\"string\": \"Hello world\"\n}");
}

SECTION("indent=4")
{
CHECK(j.dump(4) ==
"{\n \"array\": [\n 1,\n 2,\n 3,\n 4\n ],\n \"boolean\": false,\n \"null\": null,\n \"number\": 42,\n \"object\": {},\n \"string\": \"Hello world\"\n}");
"{\n \"array\": [1,2,3,4],\n \"array2d\": [\n [1,2,3],\n [4,5,6]\n ],\n \"array3d\": [\n [\n [1,2,3],\n [4,5,6]\n ],\n [\n [2,3,4],\n [5,6,7]\n ]\n ],\n \"array4d\": [\n [\n [\n [1,2,3],\n [4,5,6]\n ],\n [\n [2,3,4],\n [5,6,7]\n ]\n ],\n [\n [\n [11,12,13],\n [14,15,16]\n ],\n [\n [12,13,14],\n [15,16,17]\n ]\n ]\n ],\n \"boolean\": false,\n \"null\": null,\n \"number\": 42,\n \"object\": {},\n \"string\": \"Hello world\"\n}");
}

SECTION("indent=x")
{
CHECK(j.dump().size() == 94);
CHECK(j.dump(1).size() == 127);
CHECK(j.dump(2).size() == 142);
CHECK(j.dump(512).size() == 7792);
CHECK(j.dump().size() == 270);
CHECK(j.dump(1).size() == 422);
CHECK(j.dump(2).size() == 522);
CHECK(j.dump(512).size() == 51522);

// important test, because it yields a resize of the indent_string
// inside the dump() function
CHECK(j.dump(1024).size() == 15472);
CHECK(j.dump(1024).size() == 102722);

const auto binary = json::binary({1, 2, 3}, 128);
CHECK(binary.dump(1024).size() == 2086);
Expand Down

0 comments on commit dd0625f

Please sign in to comment.