1

I'm attempting to loop through a list of objects within a JSON to find the object with a matching KVP (in C++ using RapidJSON). I've managed to retrieve the value using a hardcoded pointer but cannot get the function GetValueByPointer(document, "PointerString") to accept the dynamic string I am building.

The JSON looks like this:

{ "_id" : { "$oid" : "5d0985973f1c0000ee000000" }, 
"Location" : [ { "lat" : "39.4005", "lon" : "-106.106"} ], 
"Weather" : [ { "timestamp" : "2019-06-05T00:00:00", ...}, { "timestamp" : "2019-06-05T01:00:00", ...}}

This works:

Document document;
document.Parse(json);
Value* a = GetValueByPointer(document, "/Weather/1/timestamp");
std::cout << a->GetString() << std::endl;

This doesn't work:

Value* a = GetValueByPointer(document, "/Weather/1/timestamp");
int i = 1;
std::string base = "/Weather/";
std::string tail = "/timestamp"; 
std::string PointerString;
std::string TSString = "";

while(TSString != "2019-06-05T09:00:00") {
    PointerString=base;
    PointerString.append(std::to_string(i));
    PointerString.append(tail);
    PointerString = "\"" + PointerString + "\"";

    Value* timestamp = GetValueByPointer(document, PointerString);
    TSString = timestamp->GetString();
    std::cout << TSString << std::endl;
    i++;
} 

The error I get no matter what I try and convert my PointerString to is:

/usr/local/include/rapidjson/pointer.h:1156:30: note:   template argument deduction/substitution failed:
MGOIO.cc:145:62: note:   mismatched types ‘const CharType [N]’ and ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’
  Value* timestamp = GetValueByPointer(document, PointerString);
                                                              ^

When I output PointerString to the screen it looks good to me:

"/Weather/1/timestamp"

Any help is most appreciated!

cccnrc
  • 1,195
  • 11
  • 27
Fishish
  • 165
  • 1
  • 10
  • You got confused while writing this code, I think. And in that state of confusion, you added the line ``PointerString = "\"" + PointerString + "\"";``. And my guess is, that it will work once you remove that line. And to make it compile, add ``.c_str()`` in the line with ``GetValueByPointer`` to ``PointerString) – BitTickler Jun 20 '19 at 00:44
  • You are very right that I was and am confused about this :) I added the quotes to more closely simulate the hardcoded string that works. I tried it without the quotes and with .c_str() on the input to the function but consistently get the same error. I haven't seen that type ‘const CharType [N]’ before but suspect it's just a symptom of a bad input value. – Fishish Jun 20 '19 at 00:57

2 Answers2

1

I solved this by switching to nlohmann JSON. Since nlohmann's pointer is built to accept a string, it was very straightforward. I substituted JSON here for how I am getting my JSON.

#include <iostream>
#include <string>
#include <fstream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

json j = json::parse(JSON);

int i = 0;
int k;
std::string base = "/Weather/";
std::string tail = "/timestamp"; 
std::string PointerString;
std::string TSString = "";

while(TSString != "2019-06-05T09:00:00") {
        PointerString=base;
        PointerString.append(std::to_string(i));
    PointerString.append(tail);
    json::json_pointer p1(PointerString);
    TSString = j.at(p1);
        std::cout << TSString << std::endl;
    std::cout << i << std::endl;
    k=i;
    i++;
} 
Fishish
  • 165
  • 1
  • 10
  • Well done! After skimming over the documentation of rapidjson, I also concluded it would not be my first choice. It appears to be a bit too overambitious and under tested. Only recently I found a similar package in another language, which seemed to be "just great" JUST for the scope of the glorious teasing blog article. The fact that rapidjson does not accept a std::string is probably a sign, that it is not very battle tested. – BitTickler Jun 20 '19 at 17:55
0

If you look at Pointer.h, you see the various templated definitions of GetValueByPointer().

template <typename T>
 typename T::ValueType* GetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) {
     return pointer.Get(root, unresolvedTokenIndex);
 }

 template <typename T>
 const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) {
     return pointer.Get(root, unresolvedTokenIndex);
 }

 template <typename T, typename CharType, size_t N>
 typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) {
     return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex);
 }

 template <typename T, typename CharType, size_t N>
 const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) {
     return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex);
 }

Now, what you hope for is implicit type conversion from a std::string -> GenericPointer<...>, which won't happen, because of the C++ rule, that there is at most 1 implicit type conversion permitted. Here, you would need std::string -> const CharType(&source)[N] -> GenericPointer<...>, which is one implicit conversion too many.

I think the easiest way to fix your predicament would be you write your own version of this function (as you probably will call it a couple of times), templated as the others, of course and which takes a const std::string & or a const std::basic_string<CharType>& and do the conversion explicitly.

That, plus removing the line I mentioned in my comment should work.

BitTickler
  • 10,905
  • 5
  • 32
  • 53
  • Thanks for the well-explained answer for why this doesn't work. In order to clarify how I action your proposed solution: Do I just define a new template which replaces ```const CharType(&source)[N]``` with ```const std::basic_string&``` or will that likely not understand how to handle a string and I really need to rewrite more code (probably beyond my skill level and time constraints)? In this case, I will look to a different tool like picojson, nlohmann, or jsoncpp that may lower the skill required on the developer end :/. – Fishish Jun 20 '19 at 14:07