Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

std::pair wrong serialization #2655

Closed
2 tasks
tiregram opened this issue Feb 23, 2021 · 8 comments
Closed
2 tasks

std::pair wrong serialization #2655

tiregram opened this issue Feb 23, 2021 · 8 comments
Labels
confirmed documentation kind: bug solution: wontfix the issue will not be fixed (either it is impossible or deemed out of scope)

Comments

@tiregram
Copy link

tiregram commented Feb 23, 2021

What is the issue you have?

My pair is not serialized as espected and a

Please describe the steps to reproduce the issue.

std::pair<std::pair<std::string, int>, std::pair<std::string, float>> p = {
      {"a", 2}, {"a", 2}}; 
  nlohmann::json j;
  j = p;
  std::cout << j << "\n"; // i get {a:2} but i want [["a":2],["a",2]]

What is the expected behavior?

i want [["a":2],["a",2]] and not {"a":2}

And what is the actual behavior instead?

i get {"a":2} and if i serialized/deserialized, I lost my data and I get a execution error:
[json.exception.type_error.304] cannot use at() with object.

Which compiler and operating system are you using?

.

  • Compiler: gcc (GCC) 10.2.0
  • Operating system: Linux 5.4.96-1-lts

Which version of the library did you use?

  • [ x] latest release version 3.9.1

If you experience a compilation error: can you compile and run the unit tests?

  • yes
  • no - please copy/paste the error message below
@Ric3cir121
Copy link

Ric3cir121 commented Mar 1, 2021 via email

@fschuetz04
Copy link

fschuetz04 commented Mar 16, 2021

This is working as intended, objects are preferred over arrays when both are possible to create from the given data. If you explicitly want to create an array, use nlohmann::json::array:

#include <iostream>

#include "json.hpp"

int main() {
  nlohmann::json j;
  j = nlohmann::json::array({{"a", 2}, {"a", 2}});
  std::cout << j << std::endl;
}

@tiregram
Copy link
Author

not this is a bug due to the utilization of the string as first argument
The std::tuple and std::pair systems is perfectly support as the exception of case when the first argument of pair is std::string.

In this situation json will consider it as a dict and not as a array.

@tiregram
Copy link
Author

with this bug a serialisation/deserialisation will failled and not provide the same c++ structure.
if one of your pair have the same value your value will be lost.
Of course a know how to make a array in json. The bug is in the transfomation of std::tuple std::pair in the json equivalent data.

@nlohmann
Copy link
Owner

Lists of pairs create JSON objects if the first element is a string. This should be documented properly as this example shows that the results may be surprising. However, changing this behavior would be a breaking change.

@nlohmann nlohmann added documentation solution: wontfix the issue will not be fixed (either it is impossible or deemed out of scope) labels Mar 23, 2021
@koniarik
Copy link

koniarik commented Mar 30, 2021

This problematic for us that have complex structures, I have a mechanism that exports a std::variant of std::tuples of various lengths, something like:

std::variant<   std::tuple<int,int,int>, std::tuple< std::string, int >, std::tuple< T, U, K, M >, .... >;

The second tuple will get serialized differently, which is problematic and counterintuitive behavior. (But I understand the backwards compability concerns)

The real problem is that even though I force it to be arrays, it is still converted to object later. That means that I can't prevent this from happening. (I would end up with one tuple being converted to object and rest being arrays)

Thanks to people on irc ##C++-general@freenode I have minimal example of the problematic situation:

#include "nlohmann/json.hpp"
#include <iostream>
#include <tuple>

using json = nlohmann::json;

struct T {
    std::tuple< std::string, int > tpl;
};

void to_json(json & j, const T & val )
{
     j = json::array({ std::get<0>(val.tpl), std::get<1>(val.tpl) });
     std::cout << "tmp res: " << j << "\n";
}

int main()
{
    T item{std::tuple< std::string, int >{"test", 123}};
    std::cout << json{item}.dump() << '\n';
}

This outputs array in tmp res and object in the latter output. In case you add just one item to the array, the conversion does not happen. The desired situation is of course that the array won't convert.

Is there some way to prevent this from happening?

@koniarik
Copy link

After some discusison on IRC, there is one way to avoid this:

std::cout << json{item}.dump() << '\n';
std::cout << json(item).dump() << '\n';

The () constructor does not convert the item into an object, {} does.
That solves my problem, but I would at least like to ask for a warning in the doc.

@nlohmann
Copy link
Owner

I added a FAQ entry for this bug: https://json.nlohmann.me/home/faq/#brace-initialization-yields-arrays

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed documentation kind: bug solution: wontfix the issue will not be fixed (either it is impossible or deemed out of scope)
Projects
None yet
Development

No branches or pull requests

5 participants