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

Add a get overload taking a parameter. #1231

Merged
merged 1 commit into from
Sep 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,12 +227,15 @@ json j_string = "this is a string";
std::string cpp_string = j_string;
// retrieve the string value (explicit JSON to std::string conversion)
auto cpp_string2 = j_string.get<std::string>();
// retrieve the string value (alternative explicit JSON to std::string conversion)
std::string cpp_string3;
j_string.get_to(cpp_string3);

// retrieve the serialized value (explicit JSON serialization)
std::string serialized_string = j_string.dump();

// output of original string
std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string.get<std::string>() << '\n';
std::cout << cpp_string << " == " << cpp_string2 << " == " << cpp_string3 << " == " << j_string.get<std::string>() << '\n';
// output of serialized value
std::cout << j_string << " == " << serialized_string << std::endl;
```
Expand Down Expand Up @@ -643,25 +646,24 @@ namespace ns {
}

void from_json(const json& j, person& p) {
p.name = j.at("name").get<std::string>();
p.address = j.at("address").get<std::string>();
p.age = j.at("age").get<int>();
j.at("name").get_to(p.name);
j.at("address").get_to(p.address);
j.at("age").get_to(p.age);
}
} // namespace ns
```

That's all! When calling the `json` constructor with your type, your custom `to_json` method will be automatically called.
Likewise, when calling `get<your_type>()`, the `from_json` method will be called.
Likewise, when calling `get<your_type>()` or `get_to(your_type&)`, the `from_json` method will be called.

Some important things:

* Those methods **MUST** be in your type's namespace (which can be the global namespace), or the library will not be able to locate them (in this example, they are in namespace `ns`, where `person` is defined).
* Those methods **MUST** be available (e.g., properly headers must be included) everywhere you use the implicit conversions. Look at [issue 1108](https://github.com/nlohmann/json/issues/1108) for errors that may occur otherwise.
* When using `get<your_type>()`, `your_type` **MUST** be [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). (There is a way to bypass this requirement described later.)
* In function `from_json`, use function [`at()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a93403e803947b86f4da2d1fb3345cf2c.html#a93403e803947b86f4da2d1fb3345cf2c) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior.
* In case your type contains several `operator=` definitions, code like `your_variable = your_json;` [may not compile](https://github.com/nlohmann/json/issues/667). You need to write `your_variable = your_json.get<decltype your_variable>();` instead.
* In case your type contains several `operator=` definitions, code like `your_variable = your_json;` [may not compile](https://github.com/nlohmann/json/issues/667). You need to write `your_variable = your_json.get<decltype(your_variable)>();` or `your_json.get_to(your_variable);` instead.
* You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these.
* Be careful with the definition order of the `from_json`/`to_json` functions: If a type `B` has a member of type `A`, you **MUST** define `to_json(A)` before `to_json(B)`. Look at [issue 561](https://github.com/nlohmann/json/issues/561) for more details.


#### How do I convert third-party types?
Expand Down
60 changes: 60 additions & 0 deletions doc/examples/get_to.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include <iostream>
#include <unordered_map>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

int main()
{
// create a JSON value with different types
json json_types =
{
{"boolean", true},
{
"number", {
{"integer", 42},
{"floating-point", 17.23}
}
},
{"string", "Hello, world!"},
{"array", {1, 2, 3, 4, 5}},
{"null", nullptr}
};

bool v1;
int v2;
short v3;
float v4;
int v5;
std::string v6;
std::vector<short> v7;
std::unordered_map<std::string, json> v8;


// use explicit conversions
json_types["boolean"].get_to(v1);
json_types["number"]["integer"].get_to(v2);
json_types["number"]["integer"].get_to(v3);
json_types["number"]["floating-point"].get_to(v4);
json_types["number"]["floating-point"].get_to(v5);
json_types["string"].get_to(v6);
json_types["array"].get_to(v7);
json_types.get_to(v8);

// print the conversion results
std::cout << v1 << '\n';
std::cout << v2 << ' ' << v3 << '\n';
std::cout << v4 << ' ' << v5 << '\n';
std::cout << v6 << '\n';

for (auto i : v7)
{
std::cout << i << ' ';
}
std::cout << "\n\n";

for (auto i : v8)
{
std::cout << i.first << ": " << i.second << '\n';
}
}
46 changes: 46 additions & 0 deletions include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2623,6 +2623,52 @@ class basic_json
return JSONSerializer<ValueTypeCV>::from_json(*this);
}

/*!
@brief get a value (explicit)

Explicit type conversion between the JSON value and a compatible value.
The value is filled into the input parameter by calling the @ref json_serializer<ValueType>
`from_json()` method.

The function is equivalent to executing
@code {.cpp}
ValueType v;
JSONSerializer<ValueType>::from_json(*this, v);
@endcode

This overloads is chosen if:
- @a ValueType is not @ref basic_json,
- @ref json_serializer<ValueType> has a `from_json()` method of the form
`void from_json(const basic_json&, ValueType&)`, and
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment seems to end in the middle of the sentence.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, will fix


@tparam ValueType the input parameter type.

@return the input parameter, allowing chaining calls.

@throw what @ref json_serializer<ValueType> `from_json()` method throws

@liveexample{The example below shows several conversions from JSON values
to other types. There a few things to note: (1) Floating-point numbers can
be converted to integers\, (2) A JSON array can be converted to a standard
`std::vector<short>`\, (3) A JSON object can be converted to C++
associative containers such as `std::unordered_map<std::string\,
json>`.,get_to}

@since version 3.3.0
*/
template<typename ValueType,
detail::enable_if_t <
not detail::is_basic_json<ValueType>::value and
detail::has_from_json<basic_json_t, ValueType>::value,
int> = 0>
ValueType & get_to(ValueType& v) const noexcept(noexcept(
JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))
{
JSONSerializer<ValueType>::from_json(*this, v);
return v;
}


/*!
@brief get a pointer value (explicit)

Expand Down
46 changes: 46 additions & 0 deletions single_include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13696,6 +13696,52 @@ class basic_json
return JSONSerializer<ValueTypeCV>::from_json(*this);
}

/*!
@brief get a value (explicit)

Explicit type conversion between the JSON value and a compatible value.
The value is filled into the input parameter by calling the @ref json_serializer<ValueType>
`from_json()` method.

The function is equivalent to executing
@code {.cpp}
ValueType v;
JSONSerializer<ValueType>::from_json(*this, v);
@endcode

This overloads is chosen if:
- @a ValueType is not @ref basic_json,
- @ref json_serializer<ValueType> has a `from_json()` method of the form
`void from_json(const basic_json&, ValueType&)`, and

@tparam ValueType the input parameter type.

@return the input parameter, allowing chaining calls.

@throw what @ref json_serializer<ValueType> `from_json()` method throws

@liveexample{The example below shows several conversions from JSON values
to other types. There a few things to note: (1) Floating-point numbers can
be converted to integers\, (2) A JSON array can be converted to a standard
`std::vector<short>`\, (3) A JSON object can be converted to C++
associative containers such as `std::unordered_map<std::string\,
json>`.,get_to}

@since version 3.3.0
*/
template<typename ValueType,
detail::enable_if_t <
not detail::is_basic_json<ValueType>::value and
detail::has_from_json<basic_json_t, ValueType>::value,
int> = 0>
ValueType & get_to(ValueType& v) const noexcept(noexcept(
JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))
{
JSONSerializer<ValueType>::from_json(*this, v);
return v;
}


/*!
@brief get a pointer value (explicit)

Expand Down
13 changes: 13 additions & 0 deletions test/src/unit-udt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,19 @@ TEST_CASE("basic usage", "[udt]")
CHECK(book == parsed_book);
}

SECTION("via explicit calls to get_to")
{
udt::person person;
udt::name name;

json person_json = big_json["contacts"][0]["person"];
CHECK(person_json.get_to(person) == sfinae_addict);

// correct reference gets returned
person_json["name"].get_to(name).m_val = "new name";
CHECK(name.m_val == "new name");
}

SECTION("implicit conversions")
{
const udt::contact_book parsed_book = big_json;
Expand Down