Add Pointer::Erase() and EraseValueByPointer()

This commit is contained in:
Milo Yip 2015-05-04 21:50:26 +08:00
parent a3e5fcf490
commit 8c01e7e1ce
3 changed files with 114 additions and 2 deletions

View File

@ -65,6 +65,12 @@ Pointer("/hello").Swap(d, x);
// { "project" : "RapidJSON", "stars" : 11, "a" : { "b" : [ null ] }, "hello" : "C++" }
// x becomes "world"
// Erase a member or element, return true if the value exists
bool success = Pointer("/a").Erase(d);
assert(success);
// { "project" : "RapidJSON", "stars" : 10 }
~~~
# Helper Functions {#HelperFunctions}
@ -88,6 +94,9 @@ Value& hello = GetValueByPointerWithDefault(d, "/hello", "world");
Value x("C++");
SwapValueByPointer(d, "/hello", x);
bool success = EraseValueByPointer(d, "/a");
assert(success);
~~~
The conventions are shown here for comparison:
@ -166,6 +175,8 @@ private:
};
~~~
`Erase()` or `EraseValueByPointer()` does not need allocator. And they return `true` if the value is erased successfully.
# Error Handling {#ErrorHandling}
A `Pointer` parses a source string in its constructor. If there is parsing error, `Pointer::IsValid()` returns false. And you can use `Pointer::GetParseErrorCode()` and `GetParseErrorOffset()` to retrieve the error information.

View File

@ -304,7 +304,7 @@ public:
RAPIDJSON_ASSERT(IsValid());
ValueType* v = &root;
bool exist = true;
for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
if (v->IsArray() && t->name[0] == '-' && t->length == 1) {
v->PushBack(Value().Move(), allocator);
v = &((*v)[v->Size() - 1]);
@ -373,7 +373,7 @@ public:
ValueType* Get(ValueType& root) const {
RAPIDJSON_ASSERT(IsValid());
ValueType* v = &root;
for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
switch (v->GetType()) {
case kObjectType:
{
@ -588,6 +588,50 @@ public:
//@}
//! Erase a value in a subtree.
/*!
\param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
\return Whether the resolved value is found and erased.
\note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false.
*/
bool Erase(ValueType& root) const {
RAPIDJSON_ASSERT(IsValid());
if (tokenCount_ == 0) // Cannot erase the root
return false;
ValueType* v = &root;
const Token* last = tokens_ + (tokenCount_ - 1);
for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
switch (v->GetType()) {
case kObjectType:
{
typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
if (m == v->MemberEnd())
return false;
if (t == last) {
v->EraseMember(m);
return true;
}
v = &m->value;
}
break;
case kArrayType:
if (t->index == kPointerInvalidIndex || t->index >= v->Size())
return false;
if (t == last) {
v->Erase(v->Begin() + t->index);
return true;
}
v = &((*v)[t->index]);
break;
default:
return false;
}
}
return false;
}
private:
//! Check whether a character should be percent-encoded.
/*!
@ -1131,6 +1175,18 @@ typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, con
return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Swap(document, value);
}
//////////////////////////////////////////////////////////////////////////////
template <typename T>
bool EraseValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer) {
return pointer.Erase(root);
}
template <typename T, typename CharType, size_t N>
bool EraseValueByPointer(T& root, const CharType(&source)[N]) {
return GenericPointer<typename T::ValueType>(source, N - 1).Erase(root);
}
//@}
RAPIDJSON_NAMESPACE_END

View File

@ -818,6 +818,21 @@ TEST(Pointer, Swap_NoAllocator) {
EXPECT_STREQ("bar", d["foo"][1].GetString());
}
TEST(Pointer, Erase) {
Document d;
d.Parse(kJson);
EXPECT_FALSE(Pointer("").Erase(d));
EXPECT_FALSE(Pointer("/foo/nonexist").Erase(d));
EXPECT_TRUE(Pointer("/foo/0").Erase(d));
EXPECT_EQ(1u, d["foo"].Size());
EXPECT_STREQ("baz", d["foo"][0].GetString());
EXPECT_TRUE(Pointer("/foo/0").Erase(d));
EXPECT_TRUE(d["foo"].Empty());
EXPECT_TRUE(Pointer("/foo").Erase(d));
EXPECT_TRUE(Pointer("/foo").Get(d) == 0);
}
TEST(Pointer, CreateValueByPointer) {
Document d;
Document::AllocatorType& a = d.GetAllocator();
@ -1319,6 +1334,36 @@ TEST(Pointer, SwapValueByPointer_NoAllocator) {
EXPECT_STREQ("baz", d["foo"][1].GetString());
}
TEST(Pointer, EraseValueByPointer_Pointer) {
Document d;
d.Parse(kJson);
EXPECT_FALSE(EraseValueByPointer(d, Pointer("")));
EXPECT_FALSE(Pointer("/foo/nonexist").Erase(d));
EXPECT_TRUE(EraseValueByPointer(d, Pointer("/foo/0")));
EXPECT_EQ(1u, d["foo"].Size());
EXPECT_STREQ("baz", d["foo"][0].GetString());
EXPECT_TRUE(EraseValueByPointer(d, Pointer("/foo/0")));
EXPECT_TRUE(d["foo"].Empty());
EXPECT_TRUE(EraseValueByPointer(d, Pointer("/foo")));
EXPECT_TRUE(Pointer("/foo").Get(d) == 0);
}
TEST(Pointer, EraseValueByPointer_String) {
Document d;
d.Parse(kJson);
EXPECT_FALSE(EraseValueByPointer(d, ""));
EXPECT_FALSE(Pointer("/foo/nonexist").Erase(d));
EXPECT_TRUE(EraseValueByPointer(d, "/foo/0"));
EXPECT_EQ(1u, d["foo"].Size());
EXPECT_STREQ("baz", d["foo"][0].GetString());
EXPECT_TRUE(EraseValueByPointer(d, "/foo/0"));
EXPECT_TRUE(d["foo"].Empty());
EXPECT_TRUE(EraseValueByPointer(d, "/foo"));
EXPECT_TRUE(Pointer("/foo").Get(d) == 0);
}
TEST(Pointer, Ambiguity) {
{
Document d;