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 check if a given key exists in a JSON object #1736

Closed
DragonOsman opened this issue Sep 3, 2019 · 8 comments
Closed

How to check if a given key exists in a JSON object #1736

DragonOsman opened this issue Sep 3, 2019 · 8 comments
Labels
kind: question solution: proposed fix a fix for the issue has been proposed and waits for confirmation

Comments

@DragonOsman
Copy link

DragonOsman commented Sep 3, 2019

  • Describe what you want to achieve.
    I want to see if a given key exists in a JSON object.

  • Describe what you tried.
    What I've tried is this:

// Performs currency conversion calculation
double calc_result(std::string_view currencykey, std::string_view currency_from, std::string_view currency_to,
	const double money_amount, const double conversion_rate)
{
	using namespace std::chrono_literals;
	using namespace std::string_literals;
	cache_storage cache{ 1h };
	const json sentry = nullptr;
	std::string mapkey{ "currency_list"s };
	json currency_list = cache.query_list(currencykey, mapkey, sentry);

	double result{};
	if (currency_list.contains(std::string(currency_from)) && currency_list.contains(std::string(currency_to)))
	{
		result = money_amount * conversion_rate;
	}
	return result;
}
  • Describe which system (OS, compiler) you are using.
    I'm using Windows 10 Single Language, version 1809 Build 17763.678. VS2017 compiler.

  • Describe which version of the library you are using (release version, develop branch).
    Release version 3.7.0.

@nlohmann
Copy link
Owner

nlohmann commented Sep 3, 2019

You can use the contains function.

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

I tried it just now; it doesn't work. I say that because here:

if (currency_list.contains(std::string{ currency_to }))
{
	result = money_amount * conversion_rate;
}

result is still 0 which means the condition didn't evaluate to true. (Note: I'm converting currency_to to std::string here because it's a std::string_view).

For reference, I'll leave a link to the updated code on GitHub here.

@nlohmann
Copy link
Owner

nlohmann commented Sep 3, 2019

Can you make sure the JSON value currency_list is an object? Otherwise, contains will always return false. This code

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

using json = nlohmann::json;

int main()
{
    json currency_list;
    currency_list["currency_to"] = 100;
    
    std::string_view currency_to = "currency_to";
    
    if (currency_list.contains(std::string{ currency_to }))
    {
        std::cout << "if is true" << std::endl;
    }
}

outputs

if is true

@DragonOsman
Copy link
Author

I think it's an object. Here's what it looks like:

{
    "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": "Bermudan Dollar",
    "BND": "Brunei Dollar",
    "BOB": "Bolivian Boliviano",
    "BRL": "Brazilian Real",
    "BSD": "Bahamian Dollar",
    "BTC": "Bitcoin",
    "BTN": "Bhutanese Ngultrum",
    "BWP": "Botswanan Pula",
    "BYN": "Belarusian Ruble",
    "BZD": "Belize Dollar",
    "CAD": "Canadian Dollar",
    "CDF": "Congolese Franc",
    "CHF": "Swiss Franc",
    "CLF": "Chilean Unit of Account (UF)",
    "CLP": "Chilean Peso",
    "CNH": "Chinese Yuan (Offshore)",
    "CNY": "Chinese Yuan",
    "COP": "Colombian Peso",
    "CRC": "Costa Rican Colón",
    "CUC": "Cuban Convertible Peso",
    "CUP": "Cuban 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": "Fijian Dollar",
    "FKP": "Falkland Islands Pound",
    "GBP": "British Pound Sterling",
    "GEL": "Georgian Lari",
    "GGP": "Guernsey Pound",
    "GHS": "Ghanaian Cedi",
    "GIP": "Gibraltar Pound",
    "GMD": "Gambian Dalasi",
    "GNF": "Guinean Franc",
    "GTQ": "Guatemalan Quetzal",
    "GYD": "Guyanaese Dollar",
    "HKD": "Hong Kong Dollar",
    "HNL": "Honduran Lempira",
    "HRK": "Croatian Kuna",
    "HTG": "Haitian Gourde",
    "HUF": "Hungarian Forint",
    "IDR": "Indonesian Rupiah",
    "ILS": "Israeli New Sheqel",
    "IMP": "Manx pound",
    "INR": "Indian Rupee",
    "IQD": "Iraqi Dinar",
    "IRR": "Iranian Rial",
    "ISK": "Icelandic Króna",
    "JEP": "Jersey Pound",
    "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": "Kazakhstani 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": "Macanese Pataca",
    "MRO": "Mauritanian Ouguiya (pre-2018)",
    "MRU": "Mauritanian Ouguiya",
    "MUR": "Mauritian Rupee",
    "MVR": "Maldivian Rufiyaa",
    "MWK": "Malawian Kwacha",
    "MXN": "Mexican Peso",
    "MYR": "Malaysian Ringgit",
    "MZN": "Mozambican Metical",
    "NAD": "Namibian Dollar",
    "NGN": "Nigerian Naira",
    "NIO": "Nicaraguan Córdoba",
    "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 Rial",
    "RON": "Romanian Leu",
    "RSD": "Serbian Dinar",
    "RUB": "Russian Ruble",
    "RWF": "Rwandan 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": "São Tomé and Príncipe Dobra (pre-2018)",
    "STN": "São Tomé and Príncipe Dobra",
    "SVC": "Salvadoran Colón",
    "SYP": "Syrian Pound",
    "SZL": "Swazi Lilangeni",
    "THB": "Thai Baht",
    "TJS": "Tajikistani Somoni",
    "TMT": "Turkmenistani Manat",
    "TND": "Tunisian Dinar",
    "TOP": "Tongan Pa'anga",
    "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 Bolívar Fuerte (Old)",
    "VES": "Venezuelan Bolívar Soberano",
    "VND": "Vietnamese Dong",
    "VUV": "Vanuatu Vatu",
    "WST": "Samoan Tala",
    "XAF": "CFA Franc BEAC",
    "XAG": "Silver Ounce",
    "XAU": "Gold Ounce",
    "XCD": "East Caribbean Dollar",
    "XDR": "Special Drawing Rights",
    "XOF": "CFA Franc BCEAO",
    "XPD": "Palladium Ounce",
    "XPF": "CFP Franc",
    "XPT": "Platinum Ounce",
    "YER": "Yemeni Rial",
    "ZAR": "South African Rand",
    "ZMW": "Zambian Kwacha",
    "ZWL": "Zimbabwean Dollar"
}

These are two functions to look at:

const json &cache_storage::query_list(std::string_view mapkey, std::string_view currencykey, const json &sentry) 
{
	using namespace std::string_literals;
	auto found{ m_cache_list.find(std::string(mapkey)) };
	boost::beast::error_code ec;
	try
	{
		if (found == m_cache_list.end() || (std::chrono::steady_clock::now() - found->second.first) > m_duration)
		{
			auto host{ "openexchangerates.org"s }, api_endpoint{ "/api/currencies.json"s };
			auto target{ api_endpoint + "?app_id="s + std::string(currencykey) };
			std::string port{ "443" };
			int version{ 11 };

			// The io_context is required for all IO
			boost::asio::io_context ioc;

			// The SSL context is required, and holds certificates
			ssl::context ctx{ ssl::context::tlsv12_client };

			// These objects perform our IO
			tcp::resolver resolver{ ioc };
			ssl::stream<tcp::socket> stream{ ioc, ctx };

			// Set SNI Hostname (many hosts need this to handshake successfully)
			if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str()))
			{
				boost::system::error_code ec{ static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category() };
				throw boost::system::system_error{ ec };
			}

			// Look up the domain name
			const auto results{ resolver.resolve(host, port) };

			// This holds the root certificate used for verification
			load_root_certificates(ctx);

			// Verify the remote server's certificate
			ctx.set_verify_mode(ssl::verify_peer);

			// Make the connection on the IP address we get from a lookup
			boost::asio::connect(stream.next_layer(), results.begin(), results.end());

			// Perform the SSL handshake
			stream.handshake(ssl::stream_base::client, ec);
			if (ec)
			{
				std::cerr << "Lines 827 and 828:\n";
				fail(ec, "handshake");
			}

			// Set up an HTTP GET request message
			http::request<http::string_body> req{ http::verb::get, target, version };
			req.set(http::field::host, host);
			req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);

			// Send the HTTP request to the remote host
			http::write(stream, req);

			// This buffer is used for reading and must be persisted
			boost::beast::flat_buffer buffer;

			// Declare a container to hold the response
			http::response<http::string_body> res;

			// Receive the HTTP response
			http::read(stream, buffer, res);
			found = m_cache_list.insert_or_assign(found, std::string(mapkey), 
				std::make_pair(std::chrono::steady_clock::now(), res.body()));

			// Gracefully close the stream
			boost::system::error_code ec;
			stream.shutdown(ec);
			if (ec == boost::asio::error::eof)
			{
				// Rationale:
				// http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
				ec.assign(0, ec.category());
			}
			if (ec)
			{
				throw boost::system::system_error{ ec };
			}

			// If we get here then the connection is closed gracefully
		}
		return found->second.second;
	}
	catch (const std::exception& e)
	{
		std::cerr << "Line 870: Error: " << e.what() << std::endl;
	}
	return sentry;
}

// Performs currency conversion calculation
double calc_result(std::string_view currencykey, std::string_view currency_to,
	const double money_amount, const double conversion_rate)
{
	using namespace std::chrono_literals;
	using namespace std::string_literals;
	cache_storage cache{ 1h };
	const json sentry = nullptr;
	std::string mapkey{ "currency_list"s };
	json currency_list = cache.query_list(currencykey, mapkey, sentry);

	double result{};
	if (currency_list.contains(std::string(currency_to)))
	{
		result = money_amount * conversion_rate;
	}
	return result;
}

For reference, I'll leave a link to the updated code on GitHub here.

@DragonOsman
Copy link
Author

Is there a way to check whether it's an object or an array?

@nlohmann
Copy link
Owner

nlohmann commented Sep 4, 2019 via email

@nlohmann
Copy link
Owner

Do you need further assitance?

@DragonOsman
Copy link
Author

No, thanks.

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

2 participants