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

Bug in ser_context::position() method #256

Closed
Alexandre-Dhalenne opened this issue Jul 3, 2020 · 1 comment
Closed

Bug in ser_context::position() method #256

Alexandre-Dhalenne opened this issue Jul 3, 2020 · 1 comment

Comments

@Alexandre-Dhalenne
Copy link

Hey there,
It's me again !
I found a bug in the position() method. It does not give the good position of the cursor when we have an other type than a string (int for instance).
(I put my whole example that retrieves position from JSON path)
Example :

#pragma once
#include <iostream>
#include <jsoncons/json.hpp>

using namespace jsoncons;

class string_locator : public jsoncons::default_json_visitor
{
    char* data_;
    std::size_t length_;
    std::string path_;
    std::string from_;
    std::vector<std::string> current_;
    std::vector<std::size_t>& positions_;
    
    std::vector < std::pair<int,int>> arrayIndexes; //Position in current_, value
    std::vector<int> arrayObjects_;
    bool check = false;
    bool alreadyUpdated = false;
public:
    using jsoncons::default_json_visitor::string_view_type;
    
    string_locator(char* data, std::size_t length,
        const std::string& path,
        const std::string& from, std::vector<std::size_t>& positions)
        : data_(data), length_(length),
        path_(path),
        from_(from),
        positions_(positions)
    {
    }

    std::string buildNormalizedPath(const std::vector<std::string>& iKeyList)
    {
        //Init
        std::string aNormalizedPath = "$";

        //For each key in the current stack
        for (auto& key : iKeyList)
        {
            aNormalizedPath += "[" + key + "]";
        }
        return aNormalizedPath;
    }

    bool custom_visit(const ser_context& context)
    {
        if (check)
        {
            arrayObjects_.push_back(current_.size());
        }
        check = false;
        std::string aNormPath;
        if (arrayObjects_.size() > 0 && arrayObjects_.back() == current_.size() && arrayIndexes.size() > 0)
        {
            auto& [pos, val] = arrayIndexes.back();
            current_.at(pos) = std::to_string(val);
            aNormPath = buildNormalizedPath(current_);
            val += 1;
        }
        else
        {
            aNormPath = buildNormalizedPath(current_);
        }
        std::cout << aNormPath << std::endl;
        if (path_ == aNormPath)
        {
            positions_.push_back(context.position());
        }
        alreadyUpdated = false;
        return true;
    }

    bool visit_begin_object(semantic_tag, const ser_context&, std::error_code&) override
    {
        //If we are in an array of objects and we are at the same depth (current_.size()) of the object 
        if (arrayObjects_.size() > 0 && arrayObjects_.back() == current_.size())
        {
            auto& [pos, val] = arrayIndexes.back();
            val += 1;
            current_.at(pos) = std::to_string(val);
            
        }else if (check)
        {
            //we have an array of objects
            //we save the size of the current stack in a vector
            //so when we are again at this size it means we need to update the index
            arrayObjects_.push_back(current_.size());
        }
        current_.emplace_back();
        return true;
    }

    bool visit_end_object(const ser_context&, std::error_code&) override
    {
        current_.pop_back();
        check = false;
        return true;
    }

    bool visit_key(const string_view_type& key, const ser_context&, std::error_code&) override
    {
        current_.back() = "'"+std::string(key)+"'";
        check = false;
        return true;
    }

    bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override
    {
        current_.emplace_back(std::to_string(0));
        arrayIndexes.emplace_back(std::make_pair(current_.size()-1,0));
        check = true;
        return true;
    }

    bool visit_end_array(const ser_context&, std::error_code& ec) override
    {
        current_.pop_back();
        arrayIndexes.pop_back();
        check = false;
        arrayObjects_.pop_back();
        return true;
    }

    bool visit_string(const string_view_type& value, jsoncons::semantic_tag, const jsoncons::ser_context& context, std::error_code&) override
    {
        return custom_visit(context);
    }

    bool visit_null(semantic_tag, const ser_context&, std::error_code& ec) override
    {
        return true;
    }

    bool visit_uint64(uint64_t, semantic_tag, const ser_context& context, std::error_code& ec) override
    {
        return custom_visit(context);
    }

    bool visit_int64(int64_t, semantic_tag, const ser_context& context, std::error_code& ec) override
    {
        return custom_visit(context);
    }

    bool visit_double(double, semantic_tag, const ser_context& context, std::error_code& ec) override
    {
        return custom_visit(context);
    }

    bool visit_bool(bool, semantic_tag, const ser_context& context, std::error_code& ec) override
    {
        return custom_visit(context);
    }
};

void update_in_place(std::string& input,
    const std::string& path)
{
    std::vector<std::size_t> positions;
    string_locator updater(input.data(), input.size(), path, "", positions);
    jsoncons::json_reader reader(jsoncons::string_view(input), updater);
    reader.read();

    for (auto it = positions.rbegin(); it != positions.rend(); ++it)
    {
        std::cout << "Position : " << *it << std::endl;
    }
}

int main()
{
    std::string input1 = R"(
      {
        "Parent": {
            "Child": {
                "Test": 4444333322221111
            }
        }
    }
  )";
    std::string input2 = R"(
      {
        "Parent": {
            "Child": {
                "Test": "4444333322221111"
            }
        }
    }
  )";
    try
    {
        update_in_place(input1, "$['Parent']['Child']['Test']");
        update_in_place(input2, "$['Parent']['Child']['Test']");
    }
    catch (std::exception& e)
    {
        std::cout << e.what() << "\n";
    }

}

Output :

 Position : 92 
 Position : 77

So it seems we are missing a position() - value.size() when value is not a string.

Thanks a lot for your work.

Regards,

@danielaparker
Copy link
Owner

danielaparker commented Jul 6, 2020

Fixed on master, but note a change in behavior for string. For all events now, including name, object, array, string, bool, and null, position points to the beginning of the value, which for name and string is at the double quote character (rather than the first character of the string.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants