Commit 8c01e7e1 authored by Milo Yip's avatar Milo Yip

Add Pointer::Erase() and EraseValueByPointer()

parent a3e5fcf4
......@@ -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.
......
......@@ -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
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment