Add parsing of URI fragment representation of JSON pointer
This commit is contained in:
parent
2ee15de4a9
commit
28f14bd68f
@ -25,7 +25,9 @@ enum PointerParseErrorCode {
|
||||
kPointerParseErrorNone = 0,
|
||||
|
||||
kPointerParseErrorTokenMustBeginWithSolidus,
|
||||
kPointerParseErrorInvalidEscape
|
||||
kPointerParseErrorInvalidEscape,
|
||||
kPointerParseErrorInvalidPercentEncoding,
|
||||
kPointerParseErrorCharacterMustPercentEncode
|
||||
};
|
||||
|
||||
template <typename ValueType, typename Allocator = CrtAllocator>
|
||||
@ -363,6 +365,12 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
//! Parse a JSON String or its URI fragment representation into tokens.
|
||||
/*!
|
||||
\param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated.
|
||||
\param length Length of the source string.
|
||||
\note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped.
|
||||
*/
|
||||
void Parse(const Ch* source, size_t length) {
|
||||
// Create own allocator if user did not supply.
|
||||
if (!allocator_)
|
||||
@ -380,7 +388,14 @@ private:
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
if (length != 0 && source[i] != '/') {
|
||||
// Detect if it is a URI fragment
|
||||
bool uriFragment = false;
|
||||
if (source[i] == '#') {
|
||||
uriFragment = true;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i != length && source[i] != '/') {
|
||||
parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus;
|
||||
goto error;
|
||||
}
|
||||
@ -395,6 +410,31 @@ private:
|
||||
|
||||
while (i < length && source[i] != '/') {
|
||||
Ch c = source[i++];
|
||||
|
||||
if (uriFragment) {
|
||||
// Decoding percent-encoding for URI fragment
|
||||
if (c == '%') {
|
||||
c = 0;
|
||||
for (int j = 0; j < 2; j++) {
|
||||
c <<= 4;
|
||||
Ch h = source[i];
|
||||
if (h >= '0' && h <= '9') c += h - '0';
|
||||
else if (h >= 'A' && h <= 'F') c += h - 'A' + 10;
|
||||
else if (h >= 'a' && h <= 'f') c += h - 'a' + 10;
|
||||
else {
|
||||
parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding;
|
||||
goto error;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if (!((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~')) {
|
||||
// RFC 3986 2.3 Unreserved Characters
|
||||
i--;
|
||||
parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
// Escaping "~0" -> '~', "~1" -> '/'
|
||||
if (c == '~') {
|
||||
|
@ -165,6 +165,184 @@ TEST(Pointer, Parse) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Pointer, Parse_URIFragment) {
|
||||
{
|
||||
Pointer p("#");
|
||||
EXPECT_TRUE(p.IsValid());
|
||||
EXPECT_EQ(0u, p.GetTokenCount());
|
||||
}
|
||||
|
||||
{
|
||||
Pointer p("#/foo");
|
||||
EXPECT_TRUE(p.IsValid());
|
||||
EXPECT_EQ(1u, p.GetTokenCount());
|
||||
EXPECT_EQ(3u, p.GetTokens()[0].length);
|
||||
EXPECT_STREQ("foo", p.GetTokens()[0].name);
|
||||
}
|
||||
|
||||
{
|
||||
Pointer p("#/foo/0");
|
||||
EXPECT_TRUE(p.IsValid());
|
||||
EXPECT_EQ(2u, p.GetTokenCount());
|
||||
EXPECT_EQ(3u, p.GetTokens()[0].length);
|
||||
EXPECT_STREQ("foo", p.GetTokens()[0].name);
|
||||
EXPECT_EQ(1u, p.GetTokens()[1].length);
|
||||
EXPECT_STREQ("0", p.GetTokens()[1].name);
|
||||
EXPECT_EQ(0u, p.GetTokens()[1].index);
|
||||
}
|
||||
|
||||
{
|
||||
// Unescape ~1
|
||||
Pointer p("#/a~1b");
|
||||
EXPECT_TRUE(p.IsValid());
|
||||
EXPECT_EQ(1u, p.GetTokenCount());
|
||||
EXPECT_EQ(3u, p.GetTokens()[0].length);
|
||||
EXPECT_STREQ("a/b", p.GetTokens()[0].name);
|
||||
}
|
||||
|
||||
{
|
||||
// Unescape ~0
|
||||
Pointer p("#/m~0n");
|
||||
EXPECT_TRUE(p.IsValid());
|
||||
EXPECT_EQ(1u, p.GetTokenCount());
|
||||
EXPECT_EQ(3u, p.GetTokens()[0].length);
|
||||
EXPECT_STREQ("m~n", p.GetTokens()[0].name);
|
||||
}
|
||||
|
||||
{
|
||||
// empty name
|
||||
Pointer p("#/");
|
||||
EXPECT_TRUE(p.IsValid());
|
||||
EXPECT_EQ(1u, p.GetTokenCount());
|
||||
EXPECT_EQ(0u, p.GetTokens()[0].length);
|
||||
EXPECT_STREQ("", p.GetTokens()[0].name);
|
||||
}
|
||||
|
||||
{
|
||||
// empty and non-empty name
|
||||
Pointer p("#//a");
|
||||
EXPECT_TRUE(p.IsValid());
|
||||
EXPECT_EQ(2u, p.GetTokenCount());
|
||||
EXPECT_EQ(0u, p.GetTokens()[0].length);
|
||||
EXPECT_STREQ("", p.GetTokens()[0].name);
|
||||
EXPECT_EQ(1u, p.GetTokens()[1].length);
|
||||
EXPECT_STREQ("a", p.GetTokens()[1].name);
|
||||
}
|
||||
|
||||
{
|
||||
// Null characters
|
||||
Pointer p("#/%00%00");
|
||||
EXPECT_TRUE(p.IsValid());
|
||||
EXPECT_EQ(1u, p.GetTokenCount());
|
||||
EXPECT_EQ(2u, p.GetTokens()[0].length);
|
||||
EXPECT_EQ('\0', p.GetTokens()[0].name[0]);
|
||||
EXPECT_EQ('\0', p.GetTokens()[0].name[1]);
|
||||
EXPECT_EQ('\0', p.GetTokens()[0].name[2]);
|
||||
}
|
||||
|
||||
{
|
||||
// Percentage Escapes
|
||||
EXPECT_STREQ("c%d", Pointer("#/c%25d").GetTokens()[0].name);
|
||||
EXPECT_STREQ("e^f", Pointer("#/e%5Ef").GetTokens()[0].name);
|
||||
EXPECT_STREQ("g|h", Pointer("#/g%7Ch").GetTokens()[0].name);
|
||||
EXPECT_STREQ("i\\j", Pointer("#/i%5Cj").GetTokens()[0].name);
|
||||
EXPECT_STREQ("k\"l", Pointer("#/k%22l").GetTokens()[0].name);
|
||||
EXPECT_STREQ(" ", Pointer("#/%20").GetTokens()[0].name);
|
||||
}
|
||||
|
||||
{
|
||||
// Valid index
|
||||
Pointer p("#/123");
|
||||
EXPECT_TRUE(p.IsValid());
|
||||
EXPECT_EQ(1u, p.GetTokenCount());
|
||||
EXPECT_STREQ("123", p.GetTokens()[0].name);
|
||||
EXPECT_EQ(123u, p.GetTokens()[0].index);
|
||||
}
|
||||
|
||||
{
|
||||
// Invalid index (with leading zero)
|
||||
Pointer p("#/01");
|
||||
EXPECT_TRUE(p.IsValid());
|
||||
EXPECT_EQ(1u, p.GetTokenCount());
|
||||
EXPECT_STREQ("01", p.GetTokens()[0].name);
|
||||
EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
|
||||
}
|
||||
|
||||
if (sizeof(SizeType) == 4) {
|
||||
// Invalid index (overflow)
|
||||
Pointer p("#/4294967296");
|
||||
EXPECT_TRUE(p.IsValid());
|
||||
EXPECT_EQ(1u, p.GetTokenCount());
|
||||
EXPECT_STREQ("4294967296", p.GetTokens()[0].name);
|
||||
EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
|
||||
}
|
||||
|
||||
{
|
||||
// kPointerParseErrorTokenMustBeginWithSolidus
|
||||
Pointer p("# ");
|
||||
EXPECT_FALSE(p.IsValid());
|
||||
EXPECT_EQ(kPointerParseErrorTokenMustBeginWithSolidus, p.GetParseErrorCode());
|
||||
EXPECT_EQ(1u, p.GetParseErrorOffset());
|
||||
}
|
||||
|
||||
{
|
||||
// kPointerParseErrorInvalidEscape
|
||||
Pointer p("#/~");
|
||||
EXPECT_FALSE(p.IsValid());
|
||||
EXPECT_EQ(kPointerParseErrorInvalidEscape, p.GetParseErrorCode());
|
||||
EXPECT_EQ(3u, p.GetParseErrorOffset());
|
||||
}
|
||||
|
||||
{
|
||||
// kPointerParseErrorInvalidEscape
|
||||
Pointer p("#/~2");
|
||||
EXPECT_FALSE(p.IsValid());
|
||||
EXPECT_EQ(kPointerParseErrorInvalidEscape, p.GetParseErrorCode());
|
||||
EXPECT_EQ(3u, p.GetParseErrorOffset());
|
||||
}
|
||||
|
||||
{
|
||||
// kPointerParseErrorInvalidPercentEncoding
|
||||
Pointer p("#/%");
|
||||
EXPECT_FALSE(p.IsValid());
|
||||
EXPECT_EQ(kPointerParseErrorInvalidPercentEncoding, p.GetParseErrorCode());
|
||||
EXPECT_EQ(3u, p.GetParseErrorOffset());
|
||||
}
|
||||
|
||||
{
|
||||
// kPointerParseErrorInvalidPercentEncoding
|
||||
Pointer p("#/%g0");
|
||||
EXPECT_FALSE(p.IsValid());
|
||||
EXPECT_EQ(kPointerParseErrorInvalidPercentEncoding, p.GetParseErrorCode());
|
||||
EXPECT_EQ(3u, p.GetParseErrorOffset());
|
||||
}
|
||||
|
||||
{
|
||||
// kPointerParseErrorInvalidPercentEncoding
|
||||
Pointer p("#/%0g");
|
||||
EXPECT_FALSE(p.IsValid());
|
||||
EXPECT_EQ(kPointerParseErrorInvalidPercentEncoding, p.GetParseErrorCode());
|
||||
EXPECT_EQ(4u, p.GetParseErrorOffset());
|
||||
}
|
||||
|
||||
{
|
||||
// kPointerParseErrorCharacterMustPercentEncode
|
||||
Pointer p("#/ ");
|
||||
EXPECT_FALSE(p.IsValid());
|
||||
EXPECT_EQ(kPointerParseErrorCharacterMustPercentEncode, p.GetParseErrorCode());
|
||||
EXPECT_EQ(2u, p.GetParseErrorOffset());
|
||||
}
|
||||
|
||||
{
|
||||
// kPointerParseErrorCharacterMustPercentEncode
|
||||
Pointer p("#/\\");
|
||||
EXPECT_FALSE(p.IsValid());
|
||||
EXPECT_EQ(kPointerParseErrorCharacterMustPercentEncode, p.GetParseErrorCode());
|
||||
EXPECT_EQ(2u, p.GetParseErrorOffset());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST(Pointer, Stringify) {
|
||||
// Test by roundtrip
|
||||
const char* sources[] = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user