nlohmann / json

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

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

Closed DragonOsman closed 5 years ago

DragonOsman commented 5 years ago
nlohmann commented 5 years ago

You can use the contains function.

DragonOsman commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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

nlohmann commented 5 years ago

There is a is_object() and is_array() function for that.

nlohmann commented 5 years ago

Do you need further assitance?

DragonOsman commented 5 years ago

No, thanks.