Skip to content

Commit a3a0bbd

Browse files
committed
Add optIdentifiers option
1 parent 7660e4d commit a3a0bbd

File tree

4 files changed

+71
-31
lines changed

4 files changed

+71
-31
lines changed

CHANGELOG.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,30 @@ Change log
33

44
This project follows [Semantic Versioning](http://semver.org/).
55

6-
1.3.0 (IN PROGRESS)
6+
1.3.0 (2025-12-19)
77
==================
88

99
### New features
1010

11-
* New optHex option (OFF by default). It allows integers in hex
12-
format (like: 0x1A, 0X2b).
11+
* The `Json::parse()` and `parse_in_place()` methods now have
12+
an additional `options` parameter, which can turn ON/OFF
13+
additional syntax that is not present in JSON standard.
14+
* New `optHex` parsing option (OFF by default). It allows integers
15+
in hex format (like: 0x1A, 0X2b).
16+
* New `optIdentifiers` parsing option (OFF by default). It allows
17+
object member names as C identifiers without quotes: {foo: "bar"}.
1318
* Added parsing options that are ON by default.
1419
Previous version supported these features, but these could not
1520
be turned off. Note that JSON standard doesn't allow these:
1621

17-
- optUniqueMembers: disallow duplicate member names within the
22+
- `optUniqueMembers`: disallow duplicate member names within the
1823
object. Apparently standard JSON allows (but not recommends)
1924
duplicates, see https://datatracker.ietf.org/doc/html/rfc8259#section-4
20-
- optTrailingComma: allow one trailing comma in arrays and objects,
25+
- `optTrailingComma`: allow one trailing comma in arrays and objects,
2126
like [1,2,].
22-
- optEmptyFraction: Allow floating point numbers with no digits after
27+
- `optEmptyFraction`: Allow floating point numbers with no digits after
2328
decimal point (like 1. or 2.e3).
24-
- optLineCommentC: allow // comments.
29+
- `optLineCommentC`: allow // comments.
2530

2631
### Changes
2732

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,17 @@ Summary of features
4242
from the input buffer rather allocating each such token in the heap.
4343
* The input is in UTF-8 format. The string tokens can contain escape sequences with
4444
[UTF-16 code points].
45-
* `//` comments are supported. Note that these are not part of the JSON standard.
4645
* Number values can be fetched as integers (32 or 64 bit) or as 64-bit float values.
46+
* Optional extended syntax that is not part of JSON standard. These can
47+
be turned ON/OFF:
48+
- `//` comments.
49+
- Check that member names are unique within the object and raise
50+
an error if not. When this feature is OFF, duplicates are allowed
51+
as per JSON standard.
52+
- Allow trailing commas in arrays and objects: `{"a": [1, 2, ], "b": 42, }`.
53+
- Allow no digits after the decimal point: `[0., -1., 2.e10]`.
54+
- Allow integers in hex format like: `[0x1A, 0X2b]`.
55+
- Allow object member names as C identifiers without quotes: `{foo: "bar"}`.
4756
* Exceptions are used to handle the errors. See [error handling].
4857
* Value validation is easy and doesn't require a JSON schema. See:
4958
- [Number range checking].

ujson.cpp

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,26 @@ class Parser
598598
return v;
599599
}
600600

601+
const char* parse_member_name()
602+
{
603+
char* end = nullptr;
604+
const char* name = parse_str();
605+
if (!name && (m_options & optIdentifiers)) {
606+
name = parse_identifier(&end);
607+
}
608+
if (!name) {
609+
throw ErrSyntax("invalid object syntax: expected member name or '}'", m_line_count);
610+
}
611+
skip_white_space();
612+
if (!skip_text(":")) {
613+
throw ErrSyntax("invalid object syntax: expected ':' after member name", m_line_count);
614+
}
615+
if (end) {
616+
*end = 0; // ensure identifier is null terminated
617+
}
618+
return name;
619+
}
620+
601621
ObjImpl* parse_val_obj(ArrImpl* parent)
602622
{
603623
ObjImpl* obj = nullptr;
@@ -606,14 +626,7 @@ class Parser
606626
skip_white_space();
607627
if (skip_text("}")) return obj;
608628
while (true) {
609-
const char* name = parse_str();
610-
if (nullptr == name) {
611-
throw ErrSyntax("invalid object syntax: expected member name or '}'", m_line_count);
612-
}
613-
skip_white_space();
614-
if (!skip_text(":")) {
615-
throw ErrSyntax("invalid object syntax: expected ':' after member name", m_line_count);
616-
}
629+
const char* name = parse_member_name();
617630
skip_white_space();
618631
size_t idx = obj->get_len();
619632
ValImpl* v = parse_val(obj);
@@ -904,6 +917,22 @@ class Parser
904917
return code;
905918
}
906919

920+
const char* parse_identifier(char** end)
921+
{
922+
char* name = nullptr;
923+
char* p = m_next;
924+
uint8_t ch = *p;
925+
if ('_' != ch && (ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) return nullptr;
926+
while (true) {
927+
ch = *(++p);
928+
if ('_' != ch && (ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z') && (ch < '0' || ch > '9')) break;
929+
}
930+
name = m_next;
931+
m_next = p;
932+
*end = p;
933+
return name;
934+
}
935+
907936
bool skip_text(const char* str)
908937
{
909938
size_t len = strlen(str);

ujson.h

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,18 @@ class Obj;
3030

3131
enum Options : uint32_t // Parse options
3232
{
33-
optUniqueMembers = 1 << 0, // Member names must be unique within the object.
34-
// On duplicates throw ErrSyntax.
35-
// If not set, duplicates are accepted, but only
36-
// the first value can be accessed by name, while other
37-
// values can be accessed only by index.
38-
39-
optTrailingComma = 1 << 1, // One comma allowed after the last array element or object member.
40-
// Example: { "a": [1, 2, ], "b": 42, }
41-
42-
optEmptyFraction = 1 << 2, // Allow floating point numbers with no digits after decimal point.
43-
// Example: [0., -1., 2.e10]
44-
45-
optLineCommentC = 1 << 3, // C style single line comment: //
46-
47-
optHex = 1 << 6, // Allow integers in hex format like: 0x1A, 0X2b
33+
optUniqueMembers = 1 << 0, // * Member names must be unique within the object.
34+
// On duplicates throw ErrSyntax.
35+
// If not set, duplicates are accepted, but only
36+
// the first value can be accessed by name, while other
37+
// values can be accessed only by index.
38+
optTrailingComma = 1 << 1, // * One comma allowed after the last array element or object member.
39+
// Example: { "a": [1, 2, ], "b": 42, }
40+
optEmptyFraction = 1 << 2, // * Allow floating point numbers with no digits after decimal point.
41+
// Example: [0., -1., 2.e10]
42+
optLineCommentC = 1 << 3, // * C style single line comment: //
43+
optHex = 1 << 6, // * Allow integers in hex format like: 0x1A, 0X2b
44+
optIdentifiers = 1 << 7, // * Allow object member names as C identifiers without quotes: {foo: "bar"}
4845

4946
optStandard = 0, // Conforms to JSON standard, no extra features are allowed.
5047
optDefault = optUniqueMembers | optTrailingComma | optEmptyFraction | optLineCommentC,

0 commit comments

Comments
 (0)