nlohmann / json

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

How to modify a value stored at a key? #1723

Closed DragonOsman closed 5 years ago

DragonOsman commented 5 years ago
DragonOsman commented 5 years ago

Okay, never mind; instead of changing the current object I could just make another one that's based on it, with one set of keys being the currency abbreviations I need. I want the currency list object to look like this:

{"rates":{"CAD":1.4741,"HKD":8.6776,"ISK":138.1,"PHP":58.008,"DKK":7.4559,"HUF":328.58,"CZK":25.769,"AUD":1.6396,"RON":4.7212,"SEK":10.7165,"IDR":15726.13,"INR":79.304,"BRL":4.5224,"RUB":72.9469,"HRK":7.394,"JPY":117.79,"THB":33.997,"CHF":1.0893,"SGD":1.536,"PLN":4.355,"BGN":1.9558,"TRY":6.3815,"CNY":7.8439,"NOK":9.9593,"NZD":1.736,"ZAR":16.8439,"USD":1.1065,"MXN":22.0041,"ILS":3.8924,"GBP":0.90453,"KRW":1341.19,"MYR":4.6362},"base":"EUR","date":"2019-08-23"}

How do I do this? I tried to do this:

json currency_list;
for (const auto& [key, value] : json::parse(res.body())["rates"].items())
{
    if (key == "CAD")
    {
        currency_list["currencies"][key] = "Canadian dollar";
    }
    …
}
currency_list["currencies"]["EUR"] = "European euro";

But this only gave me an object with that last item in it (the one with key "EUR"). So how do I do this correctly? I hope someone would help me out here. Thanks.

jaredgrubb commented 5 years ago

json currency_list = json::parse(res.body()); Then you iterate over that.

DragonOsman commented 5 years ago

Will it be possible to modify the values in currency_list with that? Since the values in it will the conversion rates from res.body().

jaredgrubb commented 5 years ago

Yes, then currency_list will have the right values... If you want "res" to get updated that is a different problem.

DragonOsman commented 5 years ago

I need a currency list where the values are the full currency names, like I said above. The res.body() object itself has a currency abbreviation to rates mapping; I don't want that. I just want the abbreviations themselves from that object.

DragonOsman commented 5 years ago

I tried to do this:

json currency_list{ json::parse(res.body()) };
for (auto j_iter{ currency_list["rates"].items().begin() }; j_iter != currency_list["rates"].items().end(); ++j_iter)
{
    if (j_iter.key() == "CAD")
    {

    }
}

But I don't know how to access and modify the values from here. Please help. Is this the right way, or is there still something wrong here?

DragonOsman commented 5 years ago

There's a "base" key with the value "EUR". I'm erasing that and want to add a new value inside the "rates" array. I want to rename that array key to "currencies" and add a new value in there "EUR: European euro".

So far I have this:

json currency_list{ json::parse(res.body()) };

for (auto j_iter{ currency_list.begin() }; j_iter != currency_list.end(); ++j_iter)
{
    if (j_iter->find("base") != j_iter->end())
    {
        j_iter->erase(j_iter->find("base"));
    }
}

for (auto j_iter{ currency_list["rates"].begin() }; j_iter != currency_list["rates"].end(); ++j_iter)
{
    if (j_iter->find("CAD") != j_iter->end())
    {
        *(j_iter->find("CAD")) = "Canadian dollar";
    }
    if (j_iter->find("USD") != j_iter->end())
    {
        *(j_iter->find("USD")) = "US dollar";
    }
}

Is it possible to rename keys? And then how do I insert the key-value pair I want to insert?

DragonOsman commented 5 years ago

The JSON object I want to make would look like this:

{"currences":{"CAD":"Canadian dollar","HKD":"Hong Kong dollar","ISK":"Icelandic krona","PHP":"Philippine peso","DKK":"Danish krone","HUF":"Hungarian forint","CZK":"Czech koruna","AUD":"Australian dollar","RON":"Romanian leu","SEK":"Swedish krona","IDR":"Indonesian rupiah","INR":"Indian rupee","BRL":"Brazilian real","RUB":"Russian rouble","HRK":"Croatian kuna","JPY":"Japanese yen","THB":"Thai baht","CHF":"Swiss franc","SGD":"Singapore dollar","PLN":"Polish zloty","BGN":"Bulgarian lev","TRY":"Tukish lira","CNY":"Chinese yuan renminbi","NOK":"Norwegian krone","NZD":"New Zealand dollar","ZAR":"South African rand","USD":"United States dollar","MXN":"Mexican peso","ILS":"Israeli shekel","GBP":"Pound sterling","KRW":"South Korean won","MYR":"Malaysian ringgit", "EUR":"European euro"}}

and the res.body() object looks like this:

{"rates":{"CAD":1.4741,"HKD":8.6776,"ISK":138.1,"PHP":58.008,"DKK":7.4559,"HUF":328.58,"CZK":25.769,"AUD":1.6396,"RON":4.7212,"SEK":10.7165,"IDR":15726.13,"INR":79.304,"BRL":4.5224,"RUB":72.9469,"HRK":7.394,"JPY":117.79,"THB":33.997,"CHF":1.0893,"SGD":1.536,"PLN":4.355,"BGN":1.9558,"TRY":6.3815,"CNY":7.8439,"NOK":9.9593,"NZD":1.736,"ZAR":16.8439,"USD":1.1065,"MXN":22.0041,"ILS":3.8924,"GBP":0.90453,"KRW":1341.19,"MYR":4.6362},"base":"EUR","date":"2019-08-23"}

I hope that makes it clearer what I want to do.

DragonOsman commented 5 years ago

I made a file like this to read into a std::map<std::string, std::tuple<std::string, std::string>> from:

currencies 
CAD:Canadian dollar
HKD:Hong Kong dollar
ISK:Icelandic krona
PHP:Philippine peso
DKK:Danish krone
HUF:Hungarian forint
CZK:Czech koruna
AUD:Australian dollar
RON:Romanian leu
SEK:Swedish krona
IDR:Indonesian rupiah
INR:Indian rupee
BRL:Brazilian real
RUB:Russian rouble
HRK:Croatian kuna
JPY:Japanese yen
THB:Thai baht
CHF:Swiss franc
SGD:Singapore dollar
PLN:Polish zloty
BGN:Bulgarian lev
TRY:Tukish lira
CNY:Chinese yuan renminbi
NOK:Norwegian krone
NZD:New Zealand dollar
ZAR:South African rand
USD:United States dollar
MXN:Mexican peso
ILS:Israeli shekel
GBP:Pound sterling
KRW:South Korean won
MYR:Malaysian ringgit
EUR:European euro

and then I read into the map and initialized a nlohmann::json object from it like this:

std::string nested_map_str{}, abbreviation{}, currency_name{};
std::ifstream ifs{"./currency_list_json.txt"};
std::map<std::string, std::tuple<std::string, std::string>> currency_map;
/*Attempting to create JSON object that looks like the following:
{"currences":{"CAD":"Canadian dollar","HKD":"Hong Kong dollar","ISK":"Icelandic krona",
"PHP":"Philippine peso","DKK":"Danish krone","HUF":"Hungarian forint","CZK":"Czech koruna",
"AUD":"Australian dollar","RON":"Romanian leu","SEK":"Swedish krona","IDR":"Indonesian rupiah",
"INR":"Indian rupee","BRL":"Brazilian real","RUB":"Russian rouble","HRK":"Croatian kuna",
"JPY":"Japanese yen","THB":"Thai baht","CHF":"Swiss franc","SGD":"Singapore dollar",
"PLN":"Polish zloty","BGN":"Bulgarian lev","TRY":"Tukish lira","CNY":"Chinese yuan renminbi",
"NOK":"Norwegian krone","NZD":"New Zealand dollar","ZAR":"South African rand","USD":"United States dollar",
"MXN":"Mexican peso","ILS":"Israeli shekel","GBP":"Pound sterling","KRW":"South Korean won","MYR":"Malaysian ringgit",
"EUR":"European euro"}}*/
while (ifs)
{
    ifs >> nested_map_str;
    std::getline(ifs, abbreviation, ':');
    std::getline(ifs, currency_name);
    currency_map.insert_or_assign(nested_map_str, std::make_tuple(abbreviation, currency_name));
}
json currency_list{ currency_map };

Then I tried to print it out, but I got this output for the JSON object:

{"{currencies:{CAD:Canadian":[" dollar,HKD","Hong Kong dollar,ISK:Icelandic krona,PHP:Philippine peso,DKK:Danish krone,HUF:Hungarian forint,CZK:Czech koruna,AUD:Australian dollar,RON:Romanian leu,SEK:Swedish krona,IDR:Indonesian rupiah,INR:Indian rupee,BRL:Brazilian real,RUB:Russian rouble,HRK:Croatian kuna,JPY:Japanese yen,THB:Thai baht,CHF:Swiss franc,SGD:Singapore dollar,PLN:Polish zloty,BGN:Bulgarian lev,TRY:Tukish lira,CNY:Chinese yuan renminbi,NOK:Norwegian krone,NZD:New Zealand dollar,ZAR:South African rand,USD:United States dollar,MXN:Mexican peso,ILS:Israeli shekel,GBP:Pound sterling,KRW:South Korean won,MYR:Malaysian ringgit, EUR:European euro}}"]}

How can I make this work correctly? Any help is appreciated. Thanks.

PapaFl commented 5 years ago

wouldn't something like this work? (can't test if this code compiles at the moment)

nlohmann::json currency_rates = nlohmann::json::parse(res.body());
nlohmann::json currency_list = nlohmann::json::object{};
for(const auto& [key, value] : currency_rates["rates"]) {
  currency_list[key] = abbrevToFullName(key);
}

But i wonder if you really need to build the conversion object dynamically; surely, it would be easier (and in my opinion cleaner) to read a file that contains the data you want, as it looks like the data is static/never changes.

DragonOsman commented 5 years ago

I had to try to find a new currency API because the one I was using most recently (before the one with the list I showed just now) seems to be a deprecated service now. At first I found one that gets its data from the European Central Bank, but it doesn't support all of world countries. I found another one that's free and supports all currencies, but this one doesn't have a way to query it for a list of currencies either. I decided to try and make a JSON object representing a list of currencies on my own. This is the function I did it with:

// Function to create and return currency list as a nlohmann::json object
json get_currency_list()
{
    return { "currencies", 
    { { "AED", "United Arab Emirates Dirham" }, { "AFN", "Afghan Afghani" },
    { "ALL", "Albanian Lek" }, { "AMD", "Armenian Dram" }, { "ANG", "Netherlands Antillean Guilder" },
    { "AOA", "Angolan Kwanza" }, { "ARS", "Argentine Peso" }, { "AUD", "Australian Dollar" },
    { "AWG", "Aruban Florin" }, { "AZN", "Azerbaijani Manat" }, { "BAM", "Bosnia & Herzegovina Convertible Mark" },
    { "BBD", "Barbadian Dollar" }, { "BDT", "Bangladeshi Taka" }, { "BGN", "Bulgarian Lev" },
    { "BHD", "Bahraini Dinar" }, { "BIF", "Burundian Franc" }, { "BMD", "Bermudian Dollar" },
    { "BND", "Brunei Dollar" }, { "BOB", "Bolivian Boliviano" }, { "BRL", "Brazilian Real" },
    { "BSD", "Bahamian Dollar" }, { "BTN", "Bhutanese Ngultrum" }, { "BWP", "Botswana Pula" },
    { "BYN", "Belarus Ruble" }, { "BZD", "Belize Dollar" }, { "CAD", "Canadian Dollar" },
    { "CDF", "Congolese Franc" }, { "CHF", "Swiss Franc" }, { "CLP", "Chilean Peso" },
    { "CNY", "Chinese Yuan" }, { "COP", "Colombian Peso" }, { "CRC", "Costa Rican Colon" },
    { "CUC", "Cuban Convertible Peso" }, { "CVE", "Cape Verdean Escudo" }, { "CZK", "Czech Republic Koruna" },
    { "DJF", "Djiboutian Franc" }, { "DKK", "Danish Krone" }, { "DOP", "Dominican Peso" },
    { "DZD", "Algerian Dinar" }, { "EGP", "Egyptian Pound" }, { "ERN", "Eritrean Nakfa" },
    { "ETB", "Ethiopian Birr" }, { "EUR", "Euro" }, { "FJD", "Fiji Dollar" },
    { "GBP", "British Pound Sterling" }, { "GEL", "Georgian Lari" }, { "GHS", "Ghanaian Cedi" },
    { "GIP", "Gibraltar Pound" }, { "GMD", "Gambian Dalasi" }, { "GNF", "Guinea Franc" },
    { "GTQ", "Guatemalan Quetzal" }, { "GYD", "Guyanaese Dollar" }, { "HKD", "Hong Kong Dollar" },
    { "HNL", "Honduran Lempira" }, { "HRK", "Croatian Kuna" }, { "HTG", "Haiti Gourde" },
    { "HUF", "Hungarian Forint" }, { "IDR", "Indonesian Rupiah" }, { "ILS", "Israeli Shekel" },
    { "INR", "Indian Rupee" }, { "IQD", "Iraqi Dinar" }, { "IRR", "Iranian Rial" },
    { "ISK", "Icelandic Krona" }, { "JMD", "Jamaican Dollar" }, { "JOD", "Jordanian Dinar" },
    { "JPY", "Japanese Yen" }, { "KES", "Kenyan Shilling" }, { "KGS", "Kyrgystani Som" },
    { "KHR", "Cambodian Riel" }, { "KMF", "Comorian Franc" }, { "KPW", "North Korean Won" },
    { "KRW", "South Korean Won" }, { "KWD", "Kuwaiti Dinar" }, { "KYD", "Cayman Islands Dollar" },
    { "KZT", "Kazakhstan Tenge" }, { "LAK", "Laotian Kip" }, { "LBP", "Lebanese Pound" },
    { "LKR", "Sri Lankan Rupee" }, { "LRD", "Liberian Dollar" }, { "LSL", "Lesotho Loti" },
    { "LYD", "Libyan Dinar" }, { "MAD", "Moroccan Dirham" }, { "MDL", "Moldovan Leu" },
    { "MGA", "Malagasy Ariary" }, { "MKD", "Macedonian Denar" }, { "MMK", "Myanma Kyat" },
    { "MNT", "Mongolian Tugrik" }, { "MOP", "Macau Pataca" }, { "MRO", "Mauritanian Ouguiya" },
    { "MUR", "Mauritian Rupee" }, { "MVR", "Maldivian Rufiyaa" }, { "MWK", "Malawi Kwacha" },
    { "MXN", "Mexican Peso" }, { "MYR", "Malaysian Ringgit" }, { "MZN", "Mozambican Metical" },
    { "NAD", "Namibian Dollar" }, { "NGN", "Nigerian Naira" }, { "NIO", "Nicaragua Cordoba" },
    { "NOK", "Norwegian Krone" }, { "NPR", "Nepalese Rupee" }, { "NZD", "New Zealand Dollar" },
    { "OMR", "Omani Rial" }, { "PAB", "Panamanian Balboa" }, { "PEN", "Peruvian Nuevo Sol" },
    { "PGK", "Papua New Guinean Kina" }, { "PHP", "Philippine Peso" }, { "PKR", "Pakistani Rupee" },
    { "PLN", "Polish Zloty" }, { "PYG", "Paraguayan Guarani" }, { "QAR", "Qatari Riyal" },
    { "RON", "Romanian Leu" }, { "RSD", "Serbian Dinar" }, { "RUB", "Russian Ruble" },
    { "RWF", "Rwanda Franc" }, { "SAR", "Saudi Riyal" }, { "SBD", "Solomon Islands Dollar" },
    { "SCR", "Seychellois Rupee" }, { "SDG", "Sudanese Pound" }, { "SEK", "Swedish Krona" },
    { "SGD", "Singapore Dollar" }, { "SHP", "Saint Helena Pound" }, { "SLL", "Sierra Leonean Leone" },
    { "SOS", "Somali Shilling" }, { "SRD", "Surinamese Dollar" }, { "SSP", "South Sudanese Pound" },
    { "STD", "Sao Tome and Principe Dobra" }, { "SYP", "Syrian Pound" }, { "SZL", "Swazi Lilangeni" },
    { "THB", "Thai Baht" }, { "TJS", "Tajikistan Somoni" }, { "TMT", "Turkmenistani Manat" },
    { "TND", "Tunisian Dinar" }, { "TOP", "Tonga Paanga" }, { "TRY", "Turkish Lira" },
    { "TTD", "Trinidad and Tobago Dollar" }, { "TWD", "New Taiwan Dollar" }, { "TZS", "Tanzanian Shilling" },
    { "UAH", "Ukrainian Hryvnia" }, { "UGX", "Ugandan Shilling" }, { "USD", "United States Dollar" },
    { "UYU", "Uruguayan Peso" }, { "UZS", "Uzbekistan Som" }, { "VEF", "Venezuelan Bolivar" },
    { "VND", "Vietnamese Dong" }, { "VUV", "Vanuatu Vatu" }, { "WST", "Samoan Tala" },
    { "XAF", "Central African CFA franc" }, { "XCD", "East Caribbean Dollar" }, { "XOF", "West African CFA franc" },
    { "XPF", "CFP Franc" }, { "YER", "Yemeni Rial" }, { "ZAR", "South African Rand" },
    { "ZMW", "Zambian Kwacha" } } 
    };
}

I tried to use the return value of this function here to send the currency list to the JS code on my app's front end:

if (req.target() == "/?q=currency_list")
{
    try
    {
        json currency_list{ get_currency_list() };
        http::response<http::string_body> res{
            std::piecewise_construct,
            std::make_tuple(std::move(std::string(currency_list))),
            std::make_tuple(http::status::ok, req.version()) };
        res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
        res.set(http::field::content_type, "application/json");
        res.content_length(res.body().size());
        res.keep_alive(req.keep_alive());
        return send(std::move(res));
    }
    catch (const json::exception& e)
    {
        std::cerr << "Line 503: Error: " << e.what() << std::endl;
    }
}

But this gave me an error saying:

[json.exception.type_error.302] type must be string, but is array

So how do I fix this error now? Should I turn my current object into a string representation of the same object (if there's a way for it to retain the keys and values it currently has)?

I'm sorry for this. This is different from the original question now too. Should I close this thread and open a new one?

nlohmann commented 5 years ago

I'm late to this, so some comments:

However, you can use this trick for std::map (from https://stackoverflow.com/a/5743682/266378):

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

using json = nlohmann::json;

int main()
{
    json j;
    j["CAD"] = 10;
    j["EUR"] = 20;
    j["USD"] = 30;

    std::cout << j << std::endl;

    const auto it = j.find("CAD");
    if (it != j.end())
    {
        std::swap(j["Canadian Dollars"], *it);
        j.erase(it);
    }

    std::cout << j << std::endl;
}

Output:

{"CAD":10,"EUR":20,"USD":30}
{"Canadian Dollars":10,"EUR":20,"USD":30}
DragonOsman commented 5 years ago

@nlohmann Thanks. But I'd also like a reply to my most recent post if that's okay. Or maybe I really should close this issue and open a new one.

I'm talking about the post right above yours.

nlohmann commented 5 years ago

Yes, please open a new issue.