1

The facts:

  • I am using VS2013 and developing in C++.
  • I am using boost API to get the values from a standard/legit json file.
  • I can't extract name4.js, name5.js and name6.js names.
  • I have searched all over stackoverflow/Google and didn't find any explanation for the below json file I am working with.

json file:

{
   "background": {
      "scripts": [ "name1.js", "name2.js", "name3.js" ]
   },
      "default_popup": "popup.html",
      "default_title": "__MSG_name__",
   "content_scripts": [ {
      "all_frames": true,
      "js": [ "name4.js", "name5.js", "name6.js" ],
      "match_about_blank": true,
      "matches": [ "http://*/*", "https://*/*" ],
      "run_at": "document_start"
   }, {
      "all_frames": true,
      "js": [ "include.postload.js" ],
      "match_about_blank": true,
      "matches": [ "http://*/*", "https://*/*" ],
      "run_at": "document_end"
   } ]
}

What works:

  • As you can see in the code below, I was able to extract "name1.js", "name2.js" and "name3.js" using "background.scripts" (the example at boost website):

    boost::property_tree::ptree doc;
    boost::property_tree::read_json("C:/Temp\\manifest.json", doc);
    std::vector<string> jsFiles;
    try{
        BOOST_FOREACH(boost::property_tree::ptree::value_type& framePair, doc.get_child("background.scripts")){
            jsFiles.push_back(framePair.second.data());
         }
    }
    catch (boost::exception const &ex){}
    

What doesn't work:

  • I want to extract the rest of the js names which are:
    1. name4.js
    2. name5.js
    3. name6.js
  • I couldn't get it to work using the below code:

    BOOST_FOREACH(boost::property_tree::ptree::value_type& framePair2, doc.get_child("content_scripts")){
    jsFiles.push_back(framePair2.second.data());
    

What I get is "" in the vector string.

I even tried jsFiles.push_back(framePair2.second.get<std::string>("js")); but it still doesn't work.

I have also tried other methods with no success.

I'd appreciate if I could get a working code because I am out of ideas.

sehe
  • 374,641
  • 47
  • 450
  • 633
OhadM
  • 4,687
  • 1
  • 47
  • 57

2 Answers2

2

get<std::string>("js") can't work because "js" has an array value.

for(auto& e : pt.get_child("content_scripts"))
    for(auto& r : e.second.get_child("js"))
        std::cout << r.second.get_value<std::string>() << "\n";

should do

Live On Coliru

#include <sstream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <iostream>

std::string const sample = R"(
{
   "background": {
      "scripts": [ "name1.js", "name2.js", "name3.js" ]
   },
      "default_popup": "popup.html",
      "default_title": "__MSG_name__",
   "content_scripts": [ {
      "all_frames": true,
      "js": [ "name4.js", "name5.js", "name6.js" ],
      "match_about_blank": true,
      "matches": [ "http://*/*", "https://*/*" ],
      "run_at": "document_start"
   }, {
      "all_frames": true,
      "js": [ "include.postload.js" ],
      "match_about_blank": true,
      "matches": [ "http://*/*", "https://*/*" ],
      "run_at": "document_end"
   } ]
})";

using boost::property_tree::ptree;
namespace j = boost::property_tree::json_parser;

int main() {
    std::istringstream iss(sample);
    ptree pt;
    j::read_json(iss, pt);

    for(auto& e : pt.get_child("content_scripts"))
        for(auto& r : e.second.get_child("js"))
            std::cout << r.second.get_value<std::string>() << "\n";
}

Prints

name4.js
name5.js
name6.js
include.postload.js
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thank you very much! i have spent almost a day understanding this issue. I have used this code (though yours is working as well): BOOST_FOREACH(boost::property_tree::ptree::value_type& framePair2, doc.get_child("content_scripts")){ BOOST_FOREACH(boost::property_tree::ptree::value_type& framePair3, framePair2.second.get_child("js")){ jsFiles.push_back(framePair3.second.data()); } } – OhadM Jan 06 '15 at 13:26
  • @OhadM it happens. Ptree's mapping onto json and xml is pretty unintuitive, IMO. Next time, draw it out on paper, it usually helps – sehe Jan 06 '15 at 13:27
1

In case you like to have an alternative method, you could use the ad-hoc parser I posted in an earlier answer (Reading JSON file with C++ and BOOST):

Live On Coliru

#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <map>

namespace qi = boost::spirit::qi;

std::string const sample = R"(
{
   "background": {
      "scripts": [ "name1.js", "name2.js", "name3.js" ]
   },
      "default_popup": "popup.html",
      "default_title": "__MSG_name__",
   "content_scripts": [ {
      "all_frames": true,
      "js": [ "name4.js", "name5.js", "name6.js" ],
      "match_about_blank": true,
      "matches": [ "http://*/*", "https://*/*" ],
      "run_at": "document_start"
   }, {
      "all_frames": true,
      "js": [ "include.postload.js" ],
      "match_about_blank": true,
      "matches": [ "http://*/*", "https://*/*" ],
      "run_at": "document_end"
   } ]
})";

namespace qd_json { // quick and dirty JSON handling
    struct null {};
    using text   = std::string;
    using value  = boost::make_recursive_variant<
            null,
            text,                                      // "string" (roughly!)
            double,                                    // number
            std::map<text, boost::recursive_variant_>, // object
            std::vector<boost::recursive_variant_>,    // array
            bool
        >::type;
    using member = std::pair<text, value>;
    using object = std::map<text, value>;
    using array  = std::vector<value>;

    template <typename It, typename Skipper = qi::space_type>
    struct grammar : qi::grammar<It, value(), Skipper>
    {
        grammar() : grammar::base_type(value_) {
            using namespace qi;

            text_   = '"' >> raw [*('\\' >> char_ | ~char_('"'))] >> '"';
            null_   = "null" >> attr(null{});
            bool_   = "true" >> attr(true) | "false" >> attr(false);
            value_  = null_ | bool_ | text_ | double_ | object_ | array_;
            member_ = text_ >> ':' >> value_;
            object_ = '{' >> -(member_ % ',') >> '}';
            array_  = '[' >> -(value_  % ',') >> ']';

            ////////////////////////////////////////
            // Bonus: properly decoding the string:
            text_   = lexeme [ '"' >> *ch_ >> '"' ];

            ch_ = +(
                    ~char_("\"\\")) [ _val += _1 ] |
                       qi::lit("\x5C") >> (               // \ (reverse solidus)
                       qi::lit("\x22") [ _val += '"'  ] | // "    quotation mark  U+0022
                       qi::lit("\x5C") [ _val += '\\' ] | // \    reverse solidus U+005C
                       qi::lit("\x2F") [ _val += '/'  ] | // /    solidus         U+002F
                       qi::lit("\x62") [ _val += '\b' ] | // b    backspace       U+0008
                       qi::lit("\x66") [ _val += '\f' ] | // f    form feed       U+000C
                       qi::lit("\x6E") [ _val += '\n' ] | // n    line feed       U+000A
                       qi::lit("\x72") [ _val += '\r' ] | // r    carriage return U+000D
                       qi::lit("\x74") [ _val += '\t' ] | // t    tab             U+0009
                       qi::lit("\x75")                    // uXXXX                U+XXXX
                            >> _4HEXDIG [ append_utf8(qi::_val, qi::_1) ]
                    );

            BOOST_SPIRIT_DEBUG_NODES((text_)(value_)(member_)(object_)(array_)(null_)(bool_))
        }
    private:
        qi::rule<It, text()>            text_, ch_;
        qi::rule<It, null()>            null_;
        qi::rule<It, bool()>            bool_;
        qi::rule<It, value(),  Skipper> value_;
        qi::rule<It, member(), Skipper> member_;
        qi::rule<It, object(), Skipper> object_;
        qi::rule<It, array(),  Skipper> array_;

        struct append_utf8_f {
            template <typename...> struct result { typedef void type; };
            template <typename String, typename Codepoint>
            void operator()(String& to, Codepoint codepoint) const {
                auto out = std::back_inserter(to);
                boost::utf8_output_iterator<decltype(out)> convert(out);
                *convert++ = codepoint;
            }
        };
        boost::phoenix::function<append_utf8_f> append_utf8;
        qi::uint_parser<uint32_t, 16, 4, 4> _4HEXDIG;
    };

    template <typename Range, typename It = typename boost::range_iterator<Range const>::type>
    value parse(Range const& input) {
        grammar<It> g;

        It first(boost::begin(input)), last(boost::end(input));
        value parsed;
        bool ok = qi::phrase_parse(first, last, g, qi::space, parsed);

        if (ok && (first == last))
            return parsed;

        throw std::runtime_error("Remaining unparsed: '" + std::string(first, last) + "'");
    }

    namespace accessors {
        static double          dbl_(qd_json::value const&v) { return boost::get<double>(v); };
        static int             int_(qd_json::value const&v) { return boost::get<double>(v); };
        static std::string     txt_(qd_json::value const&v) { return boost::get<qd_json::text>(v); };
        static qd_json::array  arr_(qd_json::value const&v) { return boost::get<qd_json::array>(v); };
        static qd_json::object obj_(qd_json::value const&v) { return boost::get<qd_json::object>(v); };
    }
}

using It = std::string::const_iterator;

int main()
{
    using namespace qd_json::accessors;

    auto root = obj_(qd_json::parse(sample));

    for(auto& o : arr_(root["content_scripts"]))
        for(auto& f : arr_(obj_(o)["js"]))
            std::cout << txt_(f) << "\n";
}

Prints

name4.js
name5.js
name6.js
include.postload.js
Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633