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

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

Closed
DragonOsman opened this issue Aug 24, 2019 · 14 comments
Closed

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

DragonOsman opened this issue Aug 24, 2019 · 14 comments
Labels
kind: question solution: proposed fix a fix for the issue has been proposed and waits for confirmation

Comments

@DragonOsman
Copy link

  • Describe what you want to achieve.
    I have a JSON object with key:value pairs where I want to change the values. It's a mapping of currency codes to exchange rates, and I want to change it to one of currency codes to full currency names.
  • Describe what you tried.
    Right now, I tried to do this, but it didn't change the value:
for (auto &[key, value] : json::parse(res.body())["rates"].items())
{
	if (key == "CAD")
	{
		value = "Canadian dollar";
	}
}
  • Describe which system (OS, compiler) you are using.
    OS is Windows 10 Home Single Language, Version 1809. Compiler is the one in MSVS 2017. I also have VS2019, but I'm not using that one with this project.
  • Describe which version of the library you are using (release version, develop branch).
    I think it's a release version, but I'm not sure how to check.
@DragonOsman
Copy link
Author

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
Copy link
Contributor

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

@DragonOsman
Copy link
Author

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
Copy link
Contributor

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

@DragonOsman
Copy link
Author

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
Copy link
Author

DragonOsman commented Aug 26, 2019

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
Copy link
Author

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
Copy link
Author

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
Copy link
Author

DragonOsman commented Aug 26, 2019

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
Copy link

PapaFl commented Aug 27, 2019

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
Copy link
Author

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
Copy link
Owner

I'm late to this, so some comments:

  • The library uses std::map internally. You cannot change keys (easily) in a std::map, so it is also not easy in the library.
  • The return value of items() is just a nice way to access the first and second members of a std::map::iterator. As the keys are constant, you cannot assign to key().

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}

@nlohmann nlohmann added the solution: proposed fix a fix for the issue has been proposed and waits for confirmation label Aug 27, 2019
@DragonOsman
Copy link
Author

DragonOsman commented Aug 27, 2019

@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
Copy link
Owner

Yes, please open a new issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: question solution: proposed fix a fix for the issue has been proposed and waits for confirmation
Projects
None yet
Development

No branches or pull requests

4 participants