Tencent / rapidjson

A fast JSON parser/generator for C++ with both SAX/DOM style API
http://rapidjson.org/
Other
14.21k stars 3.53k forks source link

Is there class Handler for key:value? #982

Open qywx opened 7 years ago

qywx commented 7 years ago

Hello people! I am looking for way to iterate over JSON document, when Handler receives keyUri : value pair, and optionally keyName : value. I did not found relevant examples.

Expected handler looks like usual handler but it handles only values and not keys and also adds additional function parameter Pointer const& p. Using the pointer it is simple to get value from another DOM.

class Handler {
public:
  bool Int( Pointer const& p, int value );
  bool Double( Pointer const& p, double val );
  // .... etc.
}

I need this to compare and synchronize values in two JSONs.

qywx commented 7 years ago

I have implemented this idea:

// Reading a message JSON with Reader (SAX-style API).
// The JSON should be an object with key-string pairs.

#include "rapidjson/reader.h"
#include "rapidjson/pointer.h"
#include "rapidjson/error/en.h"

#include <iostream>

using namespace std;
using namespace rapidjson;

/*! \class rapidjson::PointedValueHandler
    \brief Concept for receiving events from GenericReader upon parsing.
    The functions return true if no error occurs. If they return false,
    the event publisher should terminate the process.
\code
concept PointedValueHandler
{
public:  // == TYPES ==
    using Encoding = UTF8<>;
    using Ch = Encoding::Ch;
public:  // == METHODS ==
    bool Default(Pointer const& p);
    bool Null( Pointer const& p );
    bool Bool(Pointer const& p, bool b);
    bool Int(Pointer const& p, int i);
    bool Uint(Pointer const& p, unsigned i);
    bool Int64(Pointer const& p, int64_t i);
    bool Uint64(Pointer const& p, uint64_t i);
    bool Double(Pointer const& p, double d);
    bool RawNumber(Pointer const& p, const Ch* str, SizeType length, bool copy);
    bool String(Pointer const& p, const Ch* str, SizeType length, bool copy);
    bool StartObject(Pointer const& p);
    bool EndObject(Pointer const& p, SizeType memberCount);
    bool StartArray(Pointer const& p);
    bool EndArray(Pointer const& p, SizeType elementCount);
};
\endcode
*/

//! Default implementation of PointedValueHandler.
/*! This can be used as base class of any pointed reader handler.
    The sens of using it is no need to implement all methods of concept. 
    Non-overriden (read non-hidden) methods will fall back to method \c Default() 
    \note implements PointedValueHandler concept
    \note analogy to BaseReaderHandler
 */
template<typename Encoding_ = UTF8<>, typename Derived_ = void>
struct BasePointedValueHandler
{
public:  // == TYPES == 
    using Encoding = Encoding_;
    using Derived = Derived_;
    using ThisT = BasePointedValueHandler<Encoding,Derived>;
    using Ch = typename Encoding::Ch;
    // If Derived == void -> Override = ThisT, else Override = Derived
    using Override = typename internal::SelectIf<internal::IsSame<Derived, void>, ThisT, Derived>::Type;
public:  // == METHODS ==
    bool Default(Pointer const& p)                         { return true; }
    bool Null   (Pointer const& p)                         { return static_cast<Override&>(*this).Default(p); }
    bool Bool   (Pointer const& p, bool b)                 { return static_cast<Override&>(*this).Default(p,b); }
    bool Int    (Pointer const& p, int i)                  { return static_cast<Override&>(*this).Default(p,i); }
    bool Uint   (Pointer const& p, unsigned i)             { return static_cast<Override&>(*this).Default(p,i); }
    bool Int64  (Pointer const& p, int64_t i)              { return static_cast<Override&>(*this).Default(p,i); }
    bool Uint64 (Pointer const& p, uint64_t i)             { return static_cast<Override&>(*this).Default(p,i); }
    bool Double (Pointer const& p, double d)               { return static_cast<Override&>(*this).Default(p,d); }
    /// Enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length)
    bool RawNumber(Pointer const& p, const Ch* str, SizeType length, bool copy) { return static_cast<Override&>(*this).String(p,str,length,copy); }
    bool String   (Pointer const& p, const Ch* str, SizeType length, bool copy) { return static_cast<Override&>(*this).Default(p); }
    bool StartObject(Pointer const& p)                                          { return static_cast<Override&>(*this).Default(p); }
    //bool Key(const Ch* str, SizeType length, bool copy)                       { return static_cast<Override&>(*this).String(p,str,length,copy); }
    bool EndObject(Pointer const& p, SizeType memberCount)                      { return static_cast<Override&>(*this).Default(p); }
    bool StartArray(Pointer const& p)                                           { return static_cast<Override&>(*this).Default(p); }
    bool EndArray(Pointer const& p, SizeType elementCount)                      { return static_cast<Override&>(*this).Default(p); }
};

template<typename PointedValueHandler /*= BasePointedValueHandler<>*/, typename Encoding = UTF8<>>
class KeyValueSaxHandler
        // No need to inherit `BaseReaderHandler`, we simply repeat it with ALL methods.
        //: public BaseReaderHandler<Encoding, KeyValueSaxHandler<Encoding,PointedValueHandler>>
{
public:  // == TYPES == 
    //using Override = 
    //      typename internal::SelectIf<internal::IsSame<PointedValueHandler, void>, KeyValueSaxHandler, PointedValueHandler>::Type;
    using Ch = typename Encoding::Ch;
public:  // == METHODS ==
    bool Default()                                          { bool ret = true;                    pointer_.Detach(); return ret; }
    bool Null()                                             { bool ret = pvh_.Null(pointer_);     pointer_.Detach(); return ret; }
    bool Bool(bool b)                                       { bool ret = pvh_.Bool(pointer_,b);   pointer_.Detach(); return ret; }
    bool Int(int i)                                         { bool ret = pvh_.Int(pointer_,i);    pointer_.Detach(); return ret; }
    bool Uint(unsigned u)                                   { bool ret = pvh_.Uint(pointer_,u);   pointer_.Detach(); return ret; }
    bool Int64(int64_t i)                                   { bool ret = pvh_.Int64(pointer_,i);  pointer_.Detach(); return ret; }
    bool Uint64(uint64_t u)                                 { bool ret = pvh_.Uint64(pointer_,u); pointer_.Detach(); return ret; }
    bool Double(double d)                                   { bool ret = pvh_.Double(pointer_,d); pointer_.Detach(); return ret; }
    /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length)
    bool RawNumber(const Ch* str, SizeType len, bool copy)  { bool ret = pvh_.RawNumber(pointer_,str,len,copy); pointer_.Detach(); return ret; }
    bool String   (const Ch* str, SizeType len, bool copy)  { bool ret = pvh_.String(pointer_,str,len,copy);    pointer_.Detach(); return ret; }

    bool StartObject()                                      { return pvh_.StartObject(pointer_); }
    //bool Key(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); }
    bool EndObject(SizeType size)                           { bool ret = pvh_.EndObject(pointer_,size); pointer_.Detach(); return ret; }
    bool StartArray()                                       { return pvh_.StartArray(pointer_); }
    bool EndArray(SizeType size)                            { bool ret = pvh_.EndArray(pointer_,size); pointer_.Detach(); return ret; }

    bool Key(const Ch* str, SizeType length, bool copy){
        pointer_ = pointer_.Append(str,length);  //\todo Ask why this creates new object rather than modify current
        return true;
    }
protected:
    PointedValueHandler pvh_;
    Pointer pointer_;
};

/// Stringify to accustomed `std::ostream`
std::ostream& operator<<( std::ostream &os, Pointer const& p ){
    const size_t n = p.GetTokenCount();
    auto t = p.GetTokens();
    for( size_t i = 0; i<n; ++i )
        os << '/' << t[i].name;
    return os;
}

/// Stringify to accustomed `std::ostream`
template <typename ValueType, typename Allocator = CrtAllocator>
std::ostream& operator<<( std::ostream &os, GenericPointer<ValueType,Allocator> const& p ){
    const size_t n = p.GetTokenCount();
    auto t = p.GetTokens();
    for( size_t i = 0; i<n; ++i )
        os << '/' << t[i].name;
    return os;
}

// THIS MUST BE PART OF class Pointer
// Detach last \p n tokens
/*
 * @param n 
 * @param allocator 
 * @return *this
 *
template <typename ValueType, typename Allocator = CrtAllocator>
GenericPointer<ValueType,Allocator>& Detach
        ( GenericPointer<ValueType,Allocator> &p, const SizeType& n=1, Allocator* allocator = 0 )
{
    if( n <= p.tokenCount_ )
        p.tokenCount_ -= n;
    return p;
}*/

class KeyValuePrinter 
        //: public KeyValueSaxHandler<KeyValuePrinter>
{
public:
    //using Super = KeyValueSaxHandler<KeyValuePrinter>;
    //using typename Super::Ch;
    using Ch = char;
public:
    bool Default(Pointer const& p){ return true; }
    bool Null( Pointer const& p )                                                { cout << p<<":"<<"null"<<endl; return true; }
    bool Bool(Pointer const& p, bool b)                                          { cout << p<<":"<<b <<endl; return true;}
    bool Int (Pointer const& p, int i)                                           { cout << p<<":"<<i <<endl; return true;}
    bool Uint(Pointer const& p, unsigned i)                                      { cout << p<<":"<<i <<endl; return true;}
    bool Int64(Pointer const& p, int64_t i)                                      { cout << p<<":"<<i <<endl; return true;}
    bool Uint64(Pointer const& p, uint64_t i)                                    { cout << p<<":"<<i <<endl; return true;}
    bool Double(Pointer const& p, double d)                                      { cout << p<<":"<<d <<endl; return true;}
    bool RawNumber(Pointer const& p, const Ch* str, SizeType length, bool copy)  { cout << p<<":"<<string(str,length) <<endl; return true;}
    bool String(Pointer const& p, const Ch* str, SizeType length, bool copy)     { cout << p<<":"<<string(str,length) <<endl; return true;}
    bool StartObject(Pointer const& p)                                           { cout << p<<":"<<"obj" <<endl; return true;}
    //bool Key(const Ch* str, SizeType length, bool copy)                        { cout << p<<":"<<; return true;}
    bool EndObject(Pointer const& p, SizeType memberCount)                       { cout << p<<":"<<"endobj"<<endl; return true;}
    bool StartArray(Pointer const& p)                                            { cout << p<<":"<<"arr"<<endl; return true;}
    bool EndArray(Pointer const& p, SizeType elementCount)                       { cout << p<<":"<<"endarr"<<endl; return true;}
};

int main()
{
    const char* json1 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\" }";
    const char* json = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\", \"foo\" : {} }";
    Reader reader;
    KeyValueSaxHandler<KeyValuePrinter> handler;
    StringStream ss(json);
    bool success = reader.Parse(ss, handler);
    if( !success ){
        ParseErrorCode e = reader.GetParseErrorCode();
        size_t o = reader.GetErrorOffset();
        cout << "Error: " << GetParseError_En(e) << endl;;
        cout << " at offset " << o << " near '" << string(json).substr(o, 10) << "...'" << endl;
    }

}

There is one requirement: you need to update class rapidjson::Pointer in file rapidjson/pionter.h - add method Detach(). I add this code to line 300 after Append() family:

        //! Detach last \p n tokens
    /*!
     * @param n 
     * @param allocator 
     * @return *this
     */
    GenericPointer& Detach(const SizeType& n=1, Allocator* allocator = 0){
        if( n <= this->tokenCount_ )
            this->tokenCount_ -= n;
        return *this;
    }

Output:

:obj
/greeting:Hello!
/farewell:bye-bye!
/foo:obj
/foo:endobj
:endobj
qywx commented 7 years ago

May be later I will create pull request.