nlohmann / json

JSON for Modern C++
https://json.nlohmann.me
MIT License
43.15k stars 6.73k forks source link

Please add a Pretty-Print option for arrays to stay always in one line #229

Closed mireiner closed 8 years ago

mireiner commented 8 years ago

Please add a Pretty-Printing option for arrays to stay always in one line (don't add lines) if dump() parameter > -1 or std::setw()) is set. So that only other JSON types than arrays are expanded.

Example:

json j;
j["person"]["name"] = "Glory Rainbow";
j["person"]["age"] = 61;
j["person"]["mind"] = "Easy going";
j["color"]["month"] = { 1, 3, 5, 7, 8, 10, 12};
j["color"]["order"] = {"red", "orange", "yellow", "green", "blue", "indigo", "violet"};
std::cout << j.dump(4);

Wanted output:

{
    "color": {
        "month":  [1, 3, 5, 7, 8, 10, 12],
        "order": ["red", "orange", "yellow", "green", "blue", "indigo", "violet"]
    } ,
    "person": {
        "age": 61,
        "name": "Glory Rainbow",
        "mind": "Easy going"
    }
} 

Thank you!

arnaudbrejeon commented 8 years ago

I would really like to be able to improve the dump output, too. I don't have an exact idea on how to do it. I think the arrays on one-line is a good solution.

nlohmann commented 8 years ago

Let's assume that the implementation is not the issue - how would the interface to the dump function look like? Like https://docs.python.org/2/library/json.html#json.dump ?

arnaudbrejeon commented 8 years ago

I'm not convinced by having so many parameters. I wonder if the dumps should not be performed by a JsonDumper helper class. We could provide a couple of built-ins:

and users could inherit from JsonDumper to create their own specific dumpers.

It has the advantage of providing basic dump for people don't want worry much about dumping format and letting customize easily for the others.

nlohmann commented 8 years ago

I currently see two approaches to make the serialization more configurable:

Configurable Separators

One way would be to make the characters that structure a JSON value configurable these characters are

The idea would be to configure the serialization by adding whitespace to these characters. Take the following value as example:

{
    "foo": 1,
    "bar": {
        "baz": 2,
        "fob": 3
    },
    "nop": [1, 2, 3, 4]
}

For the default values (no whitespace), the result would be:

{"foo":1,"bar":{"baz":2,"fob":3},"nop":[1,2,3,4]}

Now let's assume the key/value seperator is set to " : ". Then we'd have:

{"foo" : 1,"bar" : {"baz" : 2,"fob" : 3},"nop" : [1,2,3,4]}

Similarly, we could change the value separator to ", ":

{"foo":1, "bar":{"baz":2, "fob":3}, "nop":[1, 2, 3, 4]}

We could also think of treating the value seperator differently when used inside an object or an array. For instance, let's assume we want array values to be separated by ",", but object values to be separated by ", ":

{"foo":1, "bar":{"baz":2, "fob":3}, "nop":[1,2,3,4]}

Newlines would be possible, too, but the library would add indentation:

Assume the object would be enclosed by "{\n" and "\n}", object values to be separated by ",\n", and array values to be separated by ", ":

{
    "foo":1,
    "bar":{
        "baz":2,
        "fob":3
    },
    "nop":[1, 2, 3, 4]
}

Here, the indentation would be increased for opening braces (i.e., after the newline in "{\n"), decreased for closing braces (i.e., after the newline in "\n}"), and kept as is for all other newlines.

This approach would be very flexible as it would make more or less all aspects of the serialization configurable. Additional configuration items could be:

I have no idea yet how to make a nice interface for such a dump() function, but I think the idea should be clear.

Styles

Another approach could be to define a small number of presets to choose from. That is, choose names for reasonable parameters, for instance "compact" or "pretty" for the current versions, but also other presets for things like "no newline after comma", etc.

arnaudbrejeon commented 8 years ago

I like both ideas. Maybe the configurable separators could be stored in a struct, and styles would be hard-coded values of the structure.

Something like:

struct DumpStyle {
    std::string objectValueSeparator;
    std::string objectKeySeparator;
    std::string arraySeparator;
    size_t indentation;
    ....
};

const DumpStyle compact = { ",", ":", ",", 0, ... };

string_t dump(const DumpStyle style);
mireiner commented 8 years ago

Don't give to much on my words, because I'm a JSON beginner and use it only to store single variables and arrays to JSON configuration files for a single application. Therefore I use exclusively 'dump(4)' for pretty printing.

Except from arrays I'm happy with the actual pretty printing. Because my configuration file got many arrays (with 20 and more items) the pretty printed multiline arrays are looking confusing to my eyes now. Even more on 16:9 monitors with its small vertical screen height. So I would be glad to have the option to pretty print arrays in a single line.

For my needs it would be sufficient to have a second 'style' parameter for 'dump()' with a variety of presets. This 'style' parameter for 'dump()' could be implemented as a standard parameter which can be omitted if it is not needed. And not using it 'dump()' should perform as it was before. So written code with dump() in it can be left unchanged.

arnaudbrejeon commented 8 years ago

Here is a first implementation based on the existing dump:

  struct dump_parameters {
    const std::string object_start = "{";
    const std::string object_end = "}";
    const std::string object_key_sep = ": ";
    const std::string object_value_sep = ",";
    const std::string object_empty = "";
    const unsigned int object_newline_indent = 3;

    const std::string array_start = "[";
    const std::string array_end = "]";
    const std::string array_sep = ", ";
    const std::string array_empty = "";
    const unsigned int array_newline_indent = 3;

    unsigned int current_indent = 0;
  };

  const dump_parameters compact = {"{", "}", ":", ",", "", 0,
                                   "[", "]", ",", "",  0,  0};
  const dump_parameters pretty = {"{", "}", ": ", ",", "", 3,
                                  "[", "]", ", ", "",  3,  0};
  const dump_parameters array_oneliner = {"{", "}", ": ", ",", "", 3,
                                          "[", "]", ", ", "",  0,  0};

  string_t dump(const dump_parameters &param) const {
    auto ss = std::stringstream();
    dump(ss, param);
    return ss.str();
  }

  void dump(std::ostream &o, const dump_parameters &param) const {
    // variable to hold indentation for recursive calls
    auto new_indent = param.current_indent;

    switch (m_type) {
    case value_t::object: {
      assert(m_value.object != nullptr);

      o << param.object_start;

      // increase indentation
      if (param.object_newline_indent > 0) {
        new_indent += param.object_newline_indent;
        o << "\n" << string_t(new_indent, ' ');
      }

      for (auto i = m_value.object->cbegin(); i != m_value.object->cend();
           ++i) {
        if (i != m_value.object->cbegin()) {
          o << param.object_value_sep;
          if (param.object_newline_indent > 0) {
            o << "\n" << string_t(new_indent, ' ');
          }
        }
        o << "\"" << escape_string(i->first) << "\"" << param.object_key_sep;
        auto new_param = param;
        new_param.current_indent = new_indent;
        i->second.dump(o, new_param);
      }

      if (m_value.object->empty()) {
        o << param.object_empty;
      }

      // decrease indentation
      if (param.object_newline_indent > 0) {
        new_indent -= param.object_newline_indent;
        o << "\n" << string_t(new_indent, ' ');
      }

      o << param.object_end;
      return;
    }

    case value_t::array: {
      assert(m_value.array != nullptr);

      o << param.array_start;

      // increase indentation
      if (param.array_newline_indent > 0) {
        new_indent += param.array_newline_indent;
        o << "\n" << string_t(new_indent, ' ');
      }

      for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i) {
        if (i != m_value.array->cbegin()) {
          o << param.array_sep;
          if (param.array_newline_indent > 0) {
            o << "\n" << string_t(new_indent, ' ');
          }
        }
        auto new_param = param;
        new_param.current_indent = new_indent;
        i->dump(o, new_param);
      }

      if (m_value.array->empty()) {
        o << param.array_empty;
      }

      // decrease indentation
      if (param.array_newline_indent > 0) {
        new_indent -= param.array_newline_indent;
        o << "\n" << string_t(new_indent, ' ');
      }

      o << param.array_end;
      return;
    }

    case value_t::string: {
      assert(m_value.string != nullptr);
      o << string_t("\"") << escape_string(*m_value.string) << "\"";
      return;
    }

    case value_t::boolean: {
      o << (m_value.boolean ? "true" : "false");
      return;
    }

    case value_t::number_integer: {
      o << m_value.number_integer;
      return;
    }

    case value_t::number_unsigned: {
      o << m_value.number_unsigned;
      return;
    }

    case value_t::number_float: {
      // check if number was parsed from a string
      if (m_type.bits.parsed) {
        // check if parsed number had an exponent given
        if (m_type.bits.has_exp) {
          // buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null
          // (1)
          char buf[263];
          int len;

          // handle capitalization of the exponent
          if (m_type.bits.exp_cap) {
            len = snprintf(buf, sizeof(buf), "%.*E", m_type.bits.precision,
                           m_value.number_float) +
                  1;
          } else {
            len = snprintf(buf, sizeof(buf), "%.*e", m_type.bits.precision,
                           m_value.number_float) +
                  1;
          }

          // remove '+' sign from the exponent if necessary
          if (not m_type.bits.exp_plus) {
            if (len > static_cast<int>(sizeof(buf))) {
              len = sizeof(buf);
            }
            for (int i = 0; i < len; i++) {
              if (buf[i] == '+') {
                for (; i + 1 < len; i++) {
                  buf[i] = buf[i + 1];
                }
              }
            }
          }

          o << buf;
        } else {
          // no exponent - output as a decimal
          std::stringstream ss;
          ss.imbue(std::locale(std::locale(),
                               new DecimalSeparator)); // fix locale problems
          ss << std::setprecision(m_type.bits.precision) << std::fixed
             << m_value.number_float;
          o << ss.str();
        }
      } else {
        if (m_value.number_float == 0) {
          // special case for zero to get "0.0"/"-0.0"
          o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
        } else {
          // Otherwise 6, 15 or 16 digits of precision allows
          // round-trip IEEE 754 string->float->string,
          // string->double->string or string->long double->string;
          // to be safe, we read this value from
          // std::numeric_limits<number_float_t>::digits10
          std::stringstream ss;
          ss.imbue(std::locale(std::locale(),
                               new DecimalSeparator)); // fix locale problems
          ss << std::setprecision(std::numeric_limits<double>::digits10)
             << m_value.number_float;
          o << ss.str();
        }
      }
      return;
    }

    case value_t::discarded: {
      o << "<discarded>";
      return;
    }

    case value_t::null: {
      o << "null";
      return;
    }
    }
  }

and the call:

  auto ifs = std::ifstream(filename);
  const auto json = nlohmann::json::parse(ifs);
  std::cout << json.dump(json.array_oneliner) << std::endl;
nlohmann commented 8 years ago

Hi! I implemented my proposal. Please have a look at https://github.com/nlohmann/json/tree/feature/dump. File doc/examples/dump.cpp contains an example for a call with user-defined parameters.

nlohmann commented 8 years ago

Hi @arnaudbrejeon, I only saw now that you also proposed a way to implement this. Though our approaches are similar, it seems that my realization (https://github.com/nlohmann/json/tree/feature/dump) is a bit more generic as it allows to select for each separating item whether or not to add newlines. It would be great if you could comment on this.

arnaudbrejeon commented 8 years ago

Hi, your implementation is indeed more generic than mine. It also looks better, as the code for dumping objects and arrays is much simpler now. From my perspective, it is very nice.

The improvement I would suggest is that we could cache the result of the following code s.find('\n') == std::string::npos in the lambdas. I wonder if it can not lead to performance drop for large json files. Not 100% sure, but it would be interesting to measure.

mireiner commented 8 years ago

Hi, I'm very glad the pretty-printing will be extended!

But the options are complex and therefore missing the clarity nlohmann::json is popular for.

For example is the option splitting for brackets in empty, open and closed really that usefull? Do we really need all these options in practice? Or does this complexity just lead to more confusion than it helps?

Why not just use a small number of self explanatory presets (bit flags?) to choose from, like:

object_single_line object_multi_line array_single_line array_multi_line indent_1 indent_2 indent_3 indent_4 spaced_0 spaced_1

For example using bit flags: dump(indent_4 || spaced_1 || object_multiline || array_single_line)

This will print multiline objects, single line arrays, 1 space separated and 4 space indented. This is as clear as it could be. And the programmer can leave out all or just the preset flags he doesn't need.

In contrast look at an array example of the current implementation. Can you predict the output? I wasn't able to do that and wondered about the outcome:

#include "json.hpp"
#include <set>

using namespace std;
using json = nlohmann::json;

int main()
{
    json JSON;

    std::set<int> foo = { 1,2,3,4 };

    JSON["array"] = foo;

    cout << JSON.dump(4) << "\n\n";

    json::printer pp2 = json::printer::pretty_printer();
    pp2.array_empty = "[]";
    cout << JSON.dump(4, pp2) << "\n\n";

    json::printer pp3 = json::printer::pretty_printer();
    pp3.array_comma = ", ";
    pp3.array_empty = "[]";
    cout << JSON.dump(4, pp3) << "\n\n";

    json::printer pp4 = json::printer::pretty_printer();
    pp4.array_open = "[";
    cout << JSON.dump(3, pp4) << "\n\n";

    json::printer pp5 = json::printer::pretty_printer();
    pp5.array_open = "]";
    cout << JSON.dump(3, pp5) << "\n\n";

    json::printer pp6 = json::printer::pretty_printer();
    pp6.array_open = "[";
    pp6.array_close = "]";
    cout << JSON.dump(3, pp6) << "\n\n";

    json::printer pp7 = json::printer::pretty_printer();
    pp7.array_comma = ", ";
    pp7.array_open = "[";
    pp7.array_close = "]";
    cout << JSON.dump(4, pp7) << "\n\n";

    json::printer pp8 = json::printer::pretty_printer();
    pp8.array_open = "[\n";
    cout << JSON.dump(4, pp8) << "\n\n";

    json::printer pp9 = json::printer::pretty_printer();
    pp9.array_comma = ", ";
    pp9.array_open = "[\n";
    pp9.array_close = "\n]";
    cout << JSON.dump(4, pp9) << "\n\n";

    return 0;
}

Output:

// dump(4)
{
    "array": [
        1,
        2,
        3,
        4
    ]
}

// array_empty = "[]";
{
    "array": [
        1,
        2,
        3,
        4
    ]
}

// array_comma = ", ";
// array_empty = "[]";
{
    "array": [
        1, 2, 3, 4
    ]
}

// array_open = "[";
{
   "array": [1,
   2,
   3,
   4
]
}

// array_open = "]";
{
   "array": ]1,
   2,
   3,
   4
]
}

// array_open = "[";
// array_close = "]";
{
   "array": [1,
   2,
   3,
   4   ]
}

// array_comma = ", ";
// array_open = "[";
// array_close = "]";
{
    "array": [1, 2, 3, 4]
}

// array_open = "[\n";
{
    "array": [
        1,
        2,
        3,
        4
    ]
}

// array_comma = ", ";
// array_open = "[\n";
// array_close = "\n]";
{
    "array": [
        1, 2, 3, 4
    ]
}
nlohmann commented 8 years ago

Thanks for checking, @arnaudbrejeon! Good point with the caching - I made an adjustment: https://github.com/nlohmann/json/commit/2a1d119ef8a8423a229d8ba53c8cac3a08088d43

gregmarr commented 8 years ago

Would this be better as a local variable in dump() instead of static variables inside the lambdas, both so that the cache doesn't outlive the function, and hold onto memory unnecessarily, and so it's the same cache across all the lambdas?

nlohmann commented 8 years ago

Indeed! I really need to clean up the code and add more test cases so that bugs like this cannot happen...

nlohmann commented 8 years ago

@mireiner, I understand that adding complexity is nothing we should do light-hearted. At the same time, the originally demanded possibility to configure whether or not to add a comma inside an array goes well beyond the means of Python's dump method. Therefore, I proposed to go all way and to make all aspects of serializing JSON values configurable.

There are a lot of unpolished edges that need to be discussed:

However, I think it is hard to predict what is actually needed in practice. So far, the library has a lot of (optional) extension points which can be useful for a few people, but are (hopefully) invisible to the majority that do not need them.

It would be great to hear more opinions about this! I do not want to rush this feature until it feels right. (And @mireiner is right - it currently does not)

nlohmann commented 8 years ago

Is there any further interest in this issue?

mireiner commented 8 years ago

What does 'further interest' mean? I'm still very interested in this topic. Are new pretty printing enhancements already implemented? In which version are they available? Is there any documentation or example available?

nlohmann commented 8 years ago

Nothing new is implemented, but I would have hoped for more ideas or proposals.

No approach so far really seems nice. Furthermore, when dump() is extended, these changes may not be available if the stream version std::cout << j; is used. I really would like to have a nice way to do this, because the extension alone does not justify an "ugly" fix.

whackashoe commented 8 years ago

imo this seems like something a helper library should handle. As long as someone has access to a tree of json objects it should be pretty trivial to customize how you want them printed out. It'd seem to make most sense to default to the simplest/most compact dump as that covers 99% of use cases and require people to do std::cout << prettyprint::whitespace_galore(json) or whatever. You could actually package dense and expand like @arnaudbrejeon spoke of with json and just have dump take a struct along with to do the actual dumping json.dump<whitespace>() would be nice for reading.

mireiner commented 8 years ago

hi nlohmann,

in near future I don't think we come together here.

Because my wishes are so much lesser than yours. I only ask for a tiny feature for arrays to stay always in one line. But your approach seams to be: Either go all the way and make all aspects of serializing JSON values configurable or just stay at it is now.

The other problem is that I'm a beginner / hobbiest programmer and no expert for json either. To help to develope a solution that makes all aspects of serializing configurable is bejond my skills. The only help I can provide is to make some suggestions how a more enhanced pretty printing could look like.

My suggestion was to use presets that are simple to understand and handle. And further suggested doing this by bitflags (look obove for details in my post from 9 May). I don't expect to follow my ideas. But I don't see anymore I can do here - sorry.

So I helped myself and just deleted the indention for arrays in json.hpp code. That works for my needs:

void dump(std::ostream& o,
              const bool pretty_print,
              const unsigned int indent_step,
              const unsigned int current_indent = 0) const
{
........
    case value_t::array: // line 6304
    {
        if (m_value.array->empty())
        {
            o << "[]";
            return;
        }

        o << "[";

        for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i)
        {
            if (i != m_value.array->cbegin())
            {
                o << ", ";
            }
            i->dump(o, pretty_print, indent_step, new_indent);
        }

        o << "]";
        return;
    }
........
}

So first off I'm done. But still will appreciate any enhancements on pretty printing.

nlohmann commented 8 years ago

@mireiner Thanks for checking back! I understand your request, but please understand that I must try to make everyone happy with the API (starting with myself). I shall continue to think about a nice and general interface and let you know about the updates.

dreampuf commented 7 years ago

+1

Jojendersie commented 7 years ago

As I understood there is still a chance to reopen this issue, if there is a satisfying idea.

In general I am interested in the easy array fix and will use mireiner's fix for now. However, if there is a more complex API I see another use case: formating arrays in 2D grids. An example are transformation matrices (3D) in scene description files. Those would be formated best in 3 lines with 3 values each. Also, I have projects where I store bigger matrices in json files, which of course need different line breaks... Unfortunatelly, this requires a per node configuarbility. Is there already some meta information per node? Otherwise one could encode formating options in the name or some other attribute. All that would be necessary is to reference a style-descriptor-struct in some way.

The API I think of looks like the flollowing:

json root;
root["matrix3x3"] = {1, 0, 0, 0, 1, 0, 0, 0, 1};
root["someArray"] = {"a", "b", "c", "d", "e"};

// json::printer::pretty_arraysingleline is more or less the struct from the current proposal
// All elements which do not define a new formating inherit their parent's value. I.e.
// setting the root format applies a format to all children if not stated otherwise.
root.format(json::printer::pretty_arraysingleline);
// Create a new printer without bothering with all the options. Just take the most similar
// printer and change what is needed.
auto matrix3printer = json::printer::pretty_arraysingleline;
matrix3printer.array_elements_per_line = 3;
root["matrix3x3"].format(matrix3printer);

std::out << root;
// Output looks like:
// {
//     "matrix3x3" = [1, 0, 0,
//                    0, 1, 0,
//                    0, 0, 1],
//     "someArray" = ["a", "b", "c", "d", "e"]
// }

Pros:

Drawbacks:

informarte commented 6 years ago

Guys, you should have a look at Haskell's Text.PrettyPrint.HughesPJ!

EvanED commented 6 years ago

I've got an idea and WIP plan for very flexible formatting that I think will address most, though maybe not all, of the requests in this thread and some other related issues. Conceptually it's sort of a mixture of source formatters (like astyle and clang-format) and CSS, though don't take that analogy too far. I'm actually working on it now. I'm pretty sure I could pretty easily offer it as a separate library, but at the moment I'm working on it in my fork of this repo so that I can copy detail::serializer into a new class and work from there. I've also factored out some common code from both to a primitive_serializer, but aside from adding mine as a friend of basic_json (because serializer starts in the internals), that's as intrusive as I anticipate getting.

Anyway, are you open to incorporating this? Obviously I haven't said much about what I'm actually doing; just want to know that if there's little or no chance you'd be interested, I can break it off earlier.

nlohmann commented 6 years ago

@EvanED That sounds interesting! I’d love to see progress here as long as the API remains simple and the offered features are not too “niche”. Feel free to open a pull request and start a discussion!

brentsony commented 6 years ago

Have you considered adding the ability to suppress pretty-printing for fields with certain names?

I had to hack the Serializer to stop it from formatting deeply nested, very large arrays, that looked anything, but pretty. Pretty-printing should be smart about how to format the given content (not just blindly indent everything).

DolphinDream commented 5 years ago

Was this feature finished / merged into master?

I would like to be able to save json data with single line arrays. For longer arrays this really makes a difference (e.g. for 4x4 matrices stored as a 16 element array:

"timestamp": [
      1.0,
      0.0,
      0.0,
      0.0,
      0.0,
      1.0,
      0.0,
      0.0,
      0.0,
      0.0,
      1.0,
      0.0,
      0.0,
      0.0,
      0.0,
      1.0
    ]

vs

"timestamp": [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]

For a recorded set of 1000 matrices the regular dump makes the file spread way too many lines. Plus it stores unnecessary new lines in the file.

nlohmann commented 5 years ago

@DolphinDream The library currently only supports compressed output (no additional whitespace) or pretty-printed output:

[1.0,0.0,0.0]

vs.

[
    1.0,
    0.0,
    0.0
]
ghost commented 3 years ago

For anyone looking for how to do it, just change one line in serializer.hpp dump function. If (pretty_print) { // pretty print array json... } else { // print normally }

Change " if (pretty_print) " to " if (false && pretty_print) "

ka-ba commented 3 years ago

Another line of thought for the original request (I'd be interested as well) might be treating pretty printing not as a binary issue, but as a level (uint, or something like this).

In more detail: json objects might be given a threshold which would dictate being pretty-printed from that pretty-printing level upwards (default value 1). When writing the json tree to a stream, or when dumping it, a pretty-printing level can be specified. Backwards compatibility could be achieved by translating an "old" true to MAX_VALUE of the new data type.

I can't imagine a good method for specifying a level when outputting to a stream, but it should be independent of the current std::setw() which specifies the indent.

To (re-)create threshold values when reading json from a file, maybe a pretty-printing level must also specified in that case. But I'm utterly unsure if that would be of any practical use.

alainsanguinetti commented 2 years ago

Would it be acceptable to define: pretty printing of an array of values is to print the values on the same line, pretty printing of an array of anything else means to indent each entry?

alainsanguinetti commented 2 years ago

Would it be acceptable to define: pretty printing of an array of values is to print the values on the same line, pretty printing of an array of anything else means to indent each entry?

This does what I want: Capture

nlohmann commented 2 years ago

As touching the pretty-print code would break existing libraries, I will not make such a change now. This would go into a general refactoring of the dump function which would need a more complicated configuration struct to configure such details.

akontsevich commented 2 years ago

Any chance to implement this? We have 2D arrays and each value on a single line does not look good, better have 1 array row per line.

alainsanguinetti commented 2 years ago

Any chance to implement this? We have 2D arrays and each value on a single line does not look good, better have 1 array row per line.

@akontsevich look at the code I posted, you just have to edit the hpp and you can have it locally!

akontsevich commented 2 years ago

Any chance to implement this? We have 2D arrays and each value on a single line does not look good, better have 1 array row per line.

@akontsevich look at the code I posted, you just have to edit the hpp and you can have it locally!

@alainsanguinetti what code, where?! I did like this: https://github.com/nlohmann/json/issues/229#issuecomment-899547708 but it prints 2D array in 1 line which is ugly. I need to print 1 array row per line.

alainsanguinetti commented 2 years ago

https://github.com/nlohmann/json/issues/229#issuecomment-1025735608

akontsevich commented 2 years ago

#229 (comment)

Like I said above it does not work:

t prints 2D array in 1 line which is ugly. I need to print 1 array row per line.

bikeoid commented 2 years ago

For those who want to format arrays of int as a list on one line, but break out arrays of objects on separate lines. In serializer.hpp:

                auto elementType = val.m_value.array->begin()->m_type;
                if (pretty_print && (elementType != value_t::number_integer) && (elementType != value_t::number_unsigned))
akontsevich commented 2 years ago

Does this work for 2D or 3D arrays correctly? And I use single header variant: what lines to alter there?

bikeoid commented 2 years ago

Does this work for 2D or 3D arrays correctly? And I use single header variant: what lines to alter there?

I haven't checked the behavior of 2D/3D but I would expect it would likely come out on one line. For the single header variant, replace the line in the case value_t::array: clause (currently line 16514): from if (pretty_print)

to

   auto elementType = val.m_value.array->begin()->m_type;
   if (pretty_print && (elementType != value_t::number_integer) && (elementType != value_t::number_unsigned))
akontsevich commented 2 years ago

like I said above 1 line does not work for me.

bikeoid commented 2 years ago

FYI I tested it, and it does break out 2D and 3D int arrays to separate lines/groups. The first dimension values are on a single line.

akontsevich commented 2 years ago

FYI I tested it, and it does break out 2D and 3D int arrays to separate lines/groups. The first dimension values are on a single line.

Hmm, thanks a lot, @bikeoid, almost there: it is good for 2D now and for some 3D arrays, however some 3D arrays are still 1 element per line for some reason:

           "segments": [
                [
                    [121,72,12],
                    [116,86,12]
                ],
                [
                    [116,86,12],
                    [114,93,12]
                ],
                [
                    [114,93,12],
                    [85,216,16]
                ],

another 3D:

    "obstacles": [
        [
            [
                1.0,
                1.0,
                1.0,
                1.0,

any ideas?

Ah, seems I know - data type, another check to add: && (elementType != value_t::number_float) - that is it!

gregmarr commented 2 years ago

Those are floats, add value_t::number_float to the test. While you're there, could add value_t::boolean and maybe value_t::string as well.

Alternatively, if you want to do "anything other than an object or array on a single line":

if (pretty_print && ((elementType == value_t::array) && (elementType == value_t::object)))
akontsevich commented 2 years ago

Thanks a lot @bikeoid @gregmarr above works for me, so need to patch this line: develop/single_include/nlohmann/json.hpp#L16514

My diff is:

@@ -16511,7 +16511,10 @@ class serializer
                     return;
                 }

-                if (pretty_print)
+                auto elementType = val.m_value.array->begin()->m_type;
+                if (pretty_print && (elementType != value_t::number_integer) &&
+                    (elementType != value_t::number_unsigned) &&
+                    (elementType != value_t::number_float))
                 {
                     o->write_characters("[\n", 2);

@@ -16549,7 +16552,7 @@ class serializer
                             i != val.m_value.array->cend() - 1; ++i)
                     {
                         dump(*i, false, ensure_ascii, indent_step, current_indent);
-                        o->write_character(',');
+                        o->write_characters(", ", 2);
                     }

                     // last element
jpaulos commented 2 years ago

Thanks @akontsevich for summarizing the approach in that patch -- it worked for me.

It's naive, but I think a general print behavior I want is: "Print the entire value on one line (number, string, array, object, etc). If a compound value would not fit within an X character rule width, recursively print each sub-value onto separate lines with the same logic."

That doesn't feel like an explosion of configuration parameters, and seems useful and easy to convey. In particular, setting the rule to 0 characters yields the existing pretty-print behavior, and setting the rule to infinity characters yields the existing not-pretty-print behavior. "Just" swap out the concept of a pretty print bool with a soft ruler width instead.

I appreciate that this is not necessarily the behavior everyone wants. Consider, for example, a short object containing short objects. But for almost all of the wish-list examples above there is a ruler width that would give the user what they wanted.

xiayh1992 commented 2 years ago

``> Please add a Pretty-Printing option for arrays to stay always in one line (don't add lines) if dump() parameter > -1 or std::setw()) is set. So that only other JSON types than arrays are expanded.

Example:

json j;
j["person"]["name"] = "Glory Rainbow";
j["person"]["age"] = 61;
j["person"]["mind"] = "Easy going";
j["color"]["month"] = { 1, 3, 5, 7, 8, 10, 12};
j["color"]["order"] = {"red", "orange", "yellow", "green", "blue", "indigo", "violet"};
std::cout << j.dump(4);

Wanted output:

{
    "color": {
        "month":  [1, 3, 5, 7, 8, 10, 12],
        "order": ["red", "orange", "yellow", "green", "blue", "indigo", "violet"]
    } ,
    "person": {
        "age": 61,
        "name": "Glory Rainbow",
        "mind": "Easy going"
    }
} 

Thank you!

i still don't know how to printer array in one line when array appear, can anyone get a demo to me? `#include "json.hpp"

include

include

using namespace std; using namespace nlohmann;

int main() { json J; vector foo ={1,2,3,4,5,6}; J["array"] = foo;

J["Item"] = "something"; cout << J.dump(4) <<endl;

} ` the result is : { "array":[ 1, 2, 3, .. ], "Item": "something" } it seem weird。 how to print style like this: { "array":[1,2,3,4,5] "Item": "something" }

falbrechtskirchinger commented 2 years ago

It's not supported by the library out-of-the-box. You have to modify it yourself. There're several patches in this thread with instructions you can try.