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,
|
kPointerParseErrorNone = 0,
|
||||||
|
|
||||||
kPointerParseErrorTokenMustBeginWithSolidus,
|
kPointerParseErrorTokenMustBeginWithSolidus,
|
||||||
kPointerParseErrorInvalidEscape
|
kPointerParseErrorInvalidEscape,
|
||||||
|
kPointerParseErrorInvalidPercentEncoding,
|
||||||
|
kPointerParseErrorCharacterMustPercentEncode
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename ValueType, typename Allocator = CrtAllocator>
|
template <typename ValueType, typename Allocator = CrtAllocator>
|
||||||
@ -363,6 +365,12 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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) {
|
void Parse(const Ch* source, size_t length) {
|
||||||
// Create own allocator if user did not supply.
|
// Create own allocator if user did not supply.
|
||||||
if (!allocator_)
|
if (!allocator_)
|
||||||
@ -380,7 +388,14 @@ private:
|
|||||||
|
|
||||||
size_t i = 0;
|
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;
|
parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@ -396,6 +411,31 @@ private:
|
|||||||
while (i < length && source[i] != '/') {
|
while (i < length && source[i] != '/') {
|
||||||
Ch c = 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" -> '/'
|
// Escaping "~0" -> '~', "~1" -> '/'
|
||||||
if (c == '~') {
|
if (c == '~') {
|
||||||
if (i < length) {
|
if (i < length) {
|
||||||
|
@ -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(Pointer, Stringify) {
|
||||||
// Test by roundtrip
|
// Test by roundtrip
|
||||||
const char* sources[] = {
|
const char* sources[] = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user