Skip to content

Commit

Permalink
initial changes related to Tencent#132
Browse files Browse the repository at this point in the history
Introduce the new method `Key()` into the `Handler` concept. This method is used to process the keys of an object instead of the ambiguous and overloaded `String()` method.
  • Loading branch information
Kosta-Github committed Sep 3, 2014
1 parent c6e6bca commit aef2cf2
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 42 deletions.
26 changes: 18 additions & 8 deletions doc/sax.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ struct MyHandler {
return true;
}
bool StartObject() { cout << "StartObject()" << endl; return true; }
bool Key(const char* str, SizeType length, bool copy) {
cout << "Key(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
return true;
}
bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; }
bool StartArray() { cout << "StartArray()" << endl; return true; }
bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; }
Expand Down Expand Up @@ -104,6 +108,7 @@ class Handler {
bool Double(double d);
bool String(const Ch* str, SizeType length, bool copy);
bool StartObject();
bool Key(const Ch* str, SizeType length, bool copy);
bool EndObject(SizeType memberCount);
bool StartArray();
bool EndArray(SizeType elementCount);
Expand All @@ -118,7 +123,7 @@ When the `Reader` encounters a JSON number, it chooses a suitable C++ type mappi

`String(const char* str, SizeType length, bool copy)` is called when the `Reader` encounters a string. The first parameter is pointer to the string. The second parameter is the length of the string (excluding the null terminator). Note that RapidJSON supports null character `'\0'` inside a string. If such situation happens, `strlen(str) < length`. The last `copy` indicates whether the handler needs to make a copy of the string. For normal parsing, `copy = true`. Only when *insitu* parsing is used, `copy = false`. And beware that, the character type depends on the target encoding, which will be explained later.

When the `Reader` encounters the beginning of an object, it calls `StartObject()`. An object in JSON is a set of name-value pairs. If the object contains members it first calls `String()` for the name of member, and then calls functions depending on the type of the value. These calls of name-value pairs repeats until calling `EndObject(SizeType memberCount)`. Note that the `memberCount` parameter is just an aid for the handler, user may not need this parameter.
When the `Reader` encounters the beginning of an object, it calls `StartObject()`. An object in JSON is a set of name-value pairs. If the object contains members it first calls `Key()` for the name of member, and then calls functions depending on the type of the value. These calls of name-value pairs repeats until calling `EndObject(SizeType memberCount)`. Note that the `memberCount` parameter is just an aid for the handler, user may not need this parameter.

Array is similar to object but simpler. At the beginning of an array, the `Reader` calls `BeginArary()`. If there is elements, it calls functions according to the types of element. Similarly, in the last call `EndArray(SizeType elementCount)`, the parameter `elementCount` is just an aid for the handler.

Expand Down Expand Up @@ -189,19 +194,19 @@ void main() {
Writer<StringBuffer> writer(s);
writer.StartObject();
writer.String("hello");
writer.Key("hello");
writer.String("world");
writer.String("t");
writer.Key("t");
writer.Bool(true);
writer.String("f");
writer.Key("f");
writer.Bool(false);
writer.String("n");
writer.Key("n");
writer.Null();
writer.String("i");
writer.Key("i");
writer.Uint(123);
writer.String("pi");
writer.Key("pi");
writer.Double(3.1416);
writer.String("a");
writer.Key("a");
writer.StartArray();
for (unsigned i = 0; i < 4; i++)
writer.Uint(i);
Expand Down Expand Up @@ -282,6 +287,8 @@ User may uses `Reader` to build other data structures directly. This eliminates
In the following `messagereader` example, `ParseMessages()` parses a JSON which should be an object with key-string pairs.
TODO: 2014-09-03: this example needs to be rewritten taking the new `Handler::Key()` method into account!!!
~~~~~~~~~~cpp
#include "rapidjson/reader.h"
#include "rapidjson/error/en.h"
Expand Down Expand Up @@ -390,6 +397,8 @@ As mentioned earlier, `Writer` can handle the events published by `Reader`. `con

Actually, we can add intermediate layer(s) to filter the contents of JSON via these SAX-style API. For example, `capitalize` example capitalize all strings in a JSON.

TODO: 2014-09-03: this example needs to be rewritten taking the new `Handler::Key()` method into account!!!

~~~~~~~~~~cpp
#include "rapidjson/reader.h"
#include "rapidjson/writer.h"
Expand Down Expand Up @@ -420,6 +429,7 @@ struct CapitalizeFilter {
return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string
}
bool StartObject() { return out_.StartObject(); }
bool Key(const char* str, SizeType length, bool copy) { return String(str, SizeType, copy); }
bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); }
bool StartArray() { return out_.StartArray(); }
bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); }
Expand Down
12 changes: 6 additions & 6 deletions example/capitalize/capitalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/error/en.h"
#include <vector>
#include <algorithm>
#include <cctype>

using namespace rapidjson;
Expand All @@ -25,18 +25,18 @@ struct CapitalizeFilter {
bool Uint64(uint64_t u) { return out_.Uint64(u); }
bool Double(double d) { return out_.Double(d); }
bool String(const char* str, SizeType length, bool) {
buffer_.clear();
for (SizeType i = 0; i < length; i++)
buffer_.push_back(std::toupper(str[i]));
return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string
buffer_.assign(str, str + length);
std::transform(buffer_.begin(), buffer_.end(), buffer_.begin(), [](char c) { return std::toupper(c); });
return out_.String(buffer_.data(), length, true); // true = output handler need to copy the string
}
bool StartObject() { return out_.StartObject(); }
bool Key(const char* str, SizeType length, bool copy) { return String(str, length, copy); }
bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); }
bool StartArray() { return out_.StartArray(); }
bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); }

OutputHandler& out_;
std::vector<char> buffer_;
std::string buffer_;

private:
CapitalizeFilter(const CapitalizeFilter&);
Expand Down
4 changes: 4 additions & 0 deletions example/messagereader/messagereader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ struct MessageHandler : public BaseReaderHandler<> {
}
}

bool Key(const char* str, SizeType length, bool copy) {
return String(str, length, copy);
}

bool EndObject(SizeType) { return state_ == kExpectNameOrObjectEnd; }

bool Default() { return false; } // All other events are invalid.
Expand Down
14 changes: 7 additions & 7 deletions example/serialize/serialize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ class Person {
template <typename Writer>
void Serialize(Writer& writer) const {
// This base class just write out name-value pairs, without wrapping within an object.
writer.String("name");
writer.Key("name");
writer.String(name_.c_str(), (SizeType)name_.length()); // Suppling length of string is faster.

writer.String("age");
writer.Key("age");
writer.Uint(age_);
}

Expand All @@ -41,10 +41,10 @@ class Education {
void Serialize(Writer& writer) const {
writer.StartObject();

writer.String("school");
writer.Key("school");
writer.String(school_.c_str(), (SizeType)school_.length());

writer.String("GPA");
writer.Key("GPA");
writer.Double(GPA_);

writer.EndObject();
Expand Down Expand Up @@ -75,7 +75,7 @@ class Dependent : public Person {

Person::Serialize(writer);

writer.String("education");
writer.Key("education");
if (education_)
education_->Serialize(writer);
else
Expand Down Expand Up @@ -108,10 +108,10 @@ class Employee : public Person {

Person::Serialize(writer);

writer.String("married");
writer.Key("married");
writer.Bool(married_);

writer.String(("dependents"));
writer.Key("dependents");
writer.StartArray();
for (std::vector<Dependent>::const_iterator dependentItr = dependents_.begin(); dependentItr != dependents_.end(); ++dependentItr)
dependentItr->Serialize(writer);
Expand Down
4 changes: 4 additions & 0 deletions example/simplereader/simplereader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ struct MyHandler {
return true;
}
bool StartObject() { cout << "StartObject()" << endl; return true; }
bool Key(const char* str, SizeType length, bool copy) {
cout << "Key(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
return true;
}
bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; }
bool StartArray() { cout << "StartArray()" << endl; return true; }
bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; }
Expand Down
14 changes: 7 additions & 7 deletions example/simplewriter/simplewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ int main() {
Writer<StringBuffer> writer(s);

writer.StartObject();
writer.String("hello");
writer.Key("hello");
writer.String("world");
writer.String("t");
writer.Key("t");
writer.Bool(true);
writer.String("f");
writer.Key("f");
writer.Bool(false);
writer.String("n");
writer.Key("n");
writer.Null();
writer.String("i");
writer.Key("i");
writer.Uint(123);
writer.String("pi");
writer.Key("pi");
writer.Double(3.1416);
writer.String("a");
writer.Key("a");
writer.StartArray();
for (unsigned i = 0; i < 4; i++)
writer.Uint(i);
Expand Down
4 changes: 3 additions & 1 deletion include/rapidjson/document.h
Original file line number Diff line number Diff line change
Expand Up @@ -1320,7 +1320,7 @@ int z = a[0u].GetInt(); // This works too.
if (!handler.StartObject())
return false;
for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) {
if (!handler.String(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0))
if (!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0))
return false;
if (!m->value.Accept(handler))
return false;
Expand Down Expand Up @@ -1725,6 +1725,8 @@ class GenericDocument : public GenericValue<Encoding, Allocator> {
return true;
}

bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); }

bool StartObject() { new (stack_.template Push<ValueType>()) ValueType(kObjectType); return true; }

bool EndObject(SizeType memberCount) {
Expand Down
4 changes: 4 additions & 0 deletions include/rapidjson/prettywriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding,
return Base::WriteString(str, length);
}

bool Key(const Ch* str, SizeType length, bool copy = false) {
return String(str, length, copy);
}

bool StartObject() {
PrettyPrefix(kObjectType);
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
Expand Down
25 changes: 21 additions & 4 deletions include/rapidjson/reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ concept Handler {
bool Double(double d);
bool String(const Ch* str, SizeType length, bool copy);
bool StartObject();
bool Key(const Ch* str, SizeType length, bool copy);
bool EndObject(SizeType memberCount);
bool StartArray();
bool EndArray(SizeType elementCount);
Expand Down Expand Up @@ -181,6 +182,7 @@ struct BaseReaderHandler {
bool Double(double) { return static_cast<Override&>(*this).Default(); }
bool String(const Ch*, SizeType, bool) { return static_cast<Override&>(*this).Default(); }
bool StartObject() { return static_cast<Override&>(*this).Default(); }
bool Key(const Ch*, SizeType, bool) { return static_cast<Override&>(*this).Default(); }
bool EndObject(SizeType) { return static_cast<Override&>(*this).Default(); }
bool StartArray() { return static_cast<Override&>(*this).Default(); }
bool EndArray(SizeType) { return static_cast<Override&>(*this).Default(); }
Expand Down Expand Up @@ -471,7 +473,7 @@ class GenericReader {
if (is.Peek() != '"')
RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell());

ParseString<parseFlags>(is, handler);
ParseKey<parseFlags>(is, handler);
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;

SkipWhitespace(is);
Expand Down Expand Up @@ -617,7 +619,7 @@ class GenericReader {

// Parse string and generate String event. Different code paths for kParseInsituFlag.
template<unsigned parseFlags, typename InputStream, typename Handler>
void ParseString(InputStream& is, Handler& handler) {
void ParseStringOrKey(InputStream& is, Handler& handler, bool isString) {
internal::StreamLocalCopy<InputStream> copy(is);
InputStream& s(copy.s);

Expand All @@ -634,11 +636,26 @@ class GenericReader {
StackStream stackStream(stack_);
ParseStringToStream<parseFlags, SourceEncoding, TargetEncoding>(s, stackStream);
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
if (!handler.String(stack_.template Pop<typename TargetEncoding::Ch>(stackStream.length_), stackStream.length_ - 1, true))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell());
if(isString) {
if (!handler.String(stack_.template Pop<typename TargetEncoding::Ch>(stackStream.length_), stackStream.length_ - 1, true))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell());
} else {
if (!handler.Key(stack_.template Pop<typename TargetEncoding::Ch>(stackStream.length_), stackStream.length_ - 1, true))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell());
}
}
}

template<unsigned parseFlags, typename InputStream, typename Handler>
inline void ParseString(InputStream& is, Handler& handler) {
return ParseStringOrKey<parseFlags>(is, handler, true); // true => this is a "string" value
}

template<unsigned parseFlags, typename InputStream, typename Handler>
inline void ParseKey(InputStream& is, Handler& handler) {
return ParseStringOrKey<parseFlags>(is, handler, false); // false => this is the "key" of an object
}

// Parse string to an output is
// This function handles the prefix/suffix double quotes, escaping, and optional encoding validation.
template<unsigned parseFlags, typename SEncoding, typename TEncoding, typename InputStream, typename OutputStream>
Expand Down
4 changes: 4 additions & 0 deletions include/rapidjson/writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ class Writer {
Prefix(kStringType);
return WriteString(str, length);
}

bool Key(const Ch* str, SizeType length, bool copy = false) {
return String(str, length, copy);
}

bool StartObject() {
Prefix(kObjectType);
Expand Down
22 changes: 15 additions & 7 deletions test/unittest/readertest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -506,8 +506,14 @@ struct ParseObjectHandler : BaseReaderHandler<UTF8<>, ParseObjectHandler> {
bool Double(double d) { EXPECT_EQ(12u, step_); EXPECT_EQ(3.1416, d); step_++; return true; }
bool String(const char* str, size_t, bool) {
switch(step_) {
case 1: EXPECT_STREQ("hello", str); step_++; return true;
case 2: EXPECT_STREQ("world", str); step_++; return true;
default: ADD_FAILURE(); return false;
}
}
bool StartObject() { EXPECT_EQ(0u, step_); step_++; return true; }
bool Key(const char* str, size_t, bool) {
switch(step_) {
case 1: EXPECT_STREQ("hello", str); step_++; return true;
case 3: EXPECT_STREQ("t", str); step_++; return true;
case 5: EXPECT_STREQ("f", str); step_++; return true;
case 7: EXPECT_STREQ("n", str); step_++; return true;
Expand All @@ -517,7 +523,6 @@ struct ParseObjectHandler : BaseReaderHandler<UTF8<>, ParseObjectHandler> {
default: ADD_FAILURE(); return false;
}
}
bool StartObject() { EXPECT_EQ(0u, step_); step_++; return true; }
bool EndObject(SizeType memberCount) { EXPECT_EQ(19u, step_); EXPECT_EQ(7u, memberCount); step_++; return true; }
bool StartArray() { EXPECT_EQ(14u, step_); step_++; return true; }
bool EndArray(SizeType elementCount) { EXPECT_EQ(18u, step_); EXPECT_EQ(3u, elementCount); step_++; return true; }
Expand Down Expand Up @@ -819,9 +824,10 @@ struct IterativeParsingReaderHandler {
const static int LOG_DOUBLE = -7;
const static int LOG_STRING = -8;
const static int LOG_STARTOBJECT = -9;
const static int LOG_ENDOBJECT = -10;
const static int LOG_STARTARRAY = -11;
const static int LOG_ENDARRAY = -12;
const static int LOG_KEY = -10;
const static int LOG_ENDOBJECT = -11;
const static int LOG_STARTARRAY = -12;
const static int LOG_ENDARRAY = -13;

const static size_t LogCapacity = 256;
int Logs[LogCapacity];
Expand All @@ -848,6 +854,8 @@ struct IterativeParsingReaderHandler {

bool StartObject() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STARTOBJECT; return true; }

bool Key(const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_KEY; return true; }

bool EndObject(SizeType c) {
RAPIDJSON_ASSERT(LogCount < LogCapacity);
Logs[LogCount++] = LOG_ENDOBJECT;
Expand Down Expand Up @@ -880,7 +888,7 @@ TEST(Reader, IterativeParsing_General) {
handler.LOG_STARTARRAY,
handler.LOG_INT,
handler.LOG_STARTOBJECT,
handler.LOG_STRING,
handler.LOG_KEY,
handler.LOG_STARTARRAY,
handler.LOG_INT,
handler.LOG_INT,
Expand Down Expand Up @@ -918,7 +926,7 @@ TEST(Reader, IterativeParsing_Count) {
handler.LOG_STARTOBJECT,
handler.LOG_ENDOBJECT, 0,
handler.LOG_STARTOBJECT,
handler.LOG_STRING,
handler.LOG_KEY,
handler.LOG_INT,
handler.LOG_ENDOBJECT, 1,
handler.LOG_STARTARRAY,
Expand Down
Loading

0 comments on commit aef2cf2

Please sign in to comment.