mirror of
https://github.com/dlang/phobos.git
synced 2025-04-30 23:20:29 +03:00
[styling] Put open bracket on its own line in std.json
.
This commit is contained in:
parent
ef1dd42f82
commit
b37673c16d
1 changed files with 92 additions and 46 deletions
138
std/json.d
138
std/json.d
|
@ -23,7 +23,8 @@ import std.range;
|
||||||
import std.uni : isControl;
|
import std.uni : isControl;
|
||||||
import std.utf;
|
import std.utf;
|
||||||
|
|
||||||
private {
|
private
|
||||||
|
{
|
||||||
// Prevent conflicts from these generic names
|
// Prevent conflicts from these generic names
|
||||||
alias std.utf.stride UTFStride;
|
alias std.utf.stride UTFStride;
|
||||||
alias std.utf.decode toUnicode;
|
alias std.utf.decode toUnicode;
|
||||||
|
@ -32,7 +33,8 @@ private {
|
||||||
/**
|
/**
|
||||||
JSON type enumeration
|
JSON type enumeration
|
||||||
*/
|
*/
|
||||||
enum JSON_TYPE : byte {
|
enum JSON_TYPE : byte
|
||||||
|
{
|
||||||
/// Indicates the type of a $(D JSONValue).
|
/// Indicates the type of a $(D JSONValue).
|
||||||
STRING,
|
STRING,
|
||||||
INTEGER, /// ditto
|
INTEGER, /// ditto
|
||||||
|
@ -48,8 +50,10 @@ enum JSON_TYPE : byte {
|
||||||
/**
|
/**
|
||||||
JSON value node
|
JSON value node
|
||||||
*/
|
*/
|
||||||
struct JSONValue {
|
struct JSONValue
|
||||||
union {
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
/// Value when $(D type) is $(D JSON_TYPE.STRING)
|
/// Value when $(D type) is $(D JSON_TYPE.STRING)
|
||||||
string str;
|
string str;
|
||||||
/// Value when $(D type) is $(D JSON_TYPE.INTEGER)
|
/// Value when $(D type) is $(D JSON_TYPE.INTEGER)
|
||||||
|
@ -86,7 +90,8 @@ struct JSONValue {
|
||||||
/**
|
/**
|
||||||
Parses a serialized string and returns a tree of JSON values.
|
Parses a serialized string and returns a tree of JSON values.
|
||||||
*/
|
*/
|
||||||
JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) {
|
JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T)
|
||||||
|
{
|
||||||
JSONValue root = void;
|
JSONValue root = void;
|
||||||
root.type = JSON_TYPE.NULL;
|
root.type = JSON_TYPE.NULL;
|
||||||
|
|
||||||
|
@ -96,12 +101,15 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) {
|
||||||
dchar next = 0;
|
dchar next = 0;
|
||||||
int line = 1, pos = 1;
|
int line = 1, pos = 1;
|
||||||
|
|
||||||
void error(string msg) {
|
void error(string msg)
|
||||||
|
{
|
||||||
throw new JSONException(msg, line, pos);
|
throw new JSONException(msg, line, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
dchar peekChar() {
|
dchar peekChar()
|
||||||
if(!next) {
|
{
|
||||||
|
if(!next)
|
||||||
|
{
|
||||||
if(json.empty) return '\0';
|
if(json.empty) return '\0';
|
||||||
next = json.front;
|
next = json.front;
|
||||||
json.popFront();
|
json.popFront();
|
||||||
|
@ -109,36 +117,43 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) {
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
void skipWhitespace() {
|
void skipWhitespace()
|
||||||
|
{
|
||||||
while(isWhite(peekChar())) next = 0;
|
while(isWhite(peekChar())) next = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
dchar getChar(bool SkipWhitespace = false)() {
|
dchar getChar(bool SkipWhitespace = false)()
|
||||||
|
{
|
||||||
static if(SkipWhitespace) skipWhitespace();
|
static if(SkipWhitespace) skipWhitespace();
|
||||||
|
|
||||||
dchar c = void;
|
dchar c = void;
|
||||||
if(next) {
|
if(next)
|
||||||
|
{
|
||||||
c = next;
|
c = next;
|
||||||
next = 0;
|
next = 0;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
if(json.empty) error("Unexpected end of data.");
|
if(json.empty) error("Unexpected end of data.");
|
||||||
c = json.front;
|
c = json.front;
|
||||||
json.popFront();
|
json.popFront();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(c == '\n' || (c == '\r' && peekChar() != '\n')) {
|
if(c == '\n' || (c == '\r' && peekChar() != '\n'))
|
||||||
|
{
|
||||||
line++;
|
line++;
|
||||||
pos = 1;
|
pos = 1;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
pos++;
|
pos++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c) {
|
void checkChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c)
|
||||||
|
{
|
||||||
static if(SkipWhitespace) skipWhitespace();
|
static if(SkipWhitespace) skipWhitespace();
|
||||||
auto c2 = getChar();
|
auto c2 = getChar();
|
||||||
static if(!CaseSensitive) c2 = toLower(c2);
|
static if(!CaseSensitive) c2 = toLower(c2);
|
||||||
|
@ -158,11 +173,13 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
string parseString() {
|
string parseString()
|
||||||
|
{
|
||||||
auto str = appender!string();
|
auto str = appender!string();
|
||||||
|
|
||||||
Next:
|
Next:
|
||||||
switch(peekChar()) {
|
switch(peekChar())
|
||||||
|
{
|
||||||
case '"':
|
case '"':
|
||||||
getChar();
|
getChar();
|
||||||
break;
|
break;
|
||||||
|
@ -170,7 +187,8 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) {
|
||||||
case '\\':
|
case '\\':
|
||||||
getChar();
|
getChar();
|
||||||
auto c = getChar();
|
auto c = getChar();
|
||||||
switch(c) {
|
switch(c)
|
||||||
|
{
|
||||||
case '"': str.put('"'); break;
|
case '"': str.put('"'); break;
|
||||||
case '\\': str.put('\\'); break;
|
case '\\': str.put('\\'); break;
|
||||||
case '/': str.put('/'); break;
|
case '/': str.put('/'); break;
|
||||||
|
@ -181,7 +199,8 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) {
|
||||||
case 't': str.put('\t'); break;
|
case 't': str.put('\t'); break;
|
||||||
case 'u':
|
case 'u':
|
||||||
dchar val = 0;
|
dchar val = 0;
|
||||||
foreach_reverse(i; 0 .. 4) {
|
foreach_reverse(i; 0 .. 4)
|
||||||
|
{
|
||||||
auto hex = toUpper(getChar());
|
auto hex = toUpper(getChar());
|
||||||
if(!isHexDigit(hex)) error("Expecting hex character");
|
if(!isHexDigit(hex)) error("Expecting hex character");
|
||||||
val += (isDigit(hex) ? hex - '0' : hex - ('A' - 10)) << (4 * i);
|
val += (isDigit(hex) ? hex - '0' : hex - ('A' - 10)) << (4 * i);
|
||||||
|
@ -204,28 +223,32 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) {
|
||||||
return str.data;
|
return str.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseValue(JSONValue* value) {
|
void parseValue(JSONValue* value)
|
||||||
|
{
|
||||||
depth++;
|
depth++;
|
||||||
|
|
||||||
if(maxDepth != -1 && depth > maxDepth) error("Nesting too deep.");
|
if(maxDepth != -1 && depth > maxDepth) error("Nesting too deep.");
|
||||||
|
|
||||||
auto c = getChar!true();
|
auto c = getChar!true();
|
||||||
|
|
||||||
switch(c) {
|
switch(c)
|
||||||
|
{
|
||||||
case '{':
|
case '{':
|
||||||
value.type = JSON_TYPE.OBJECT;
|
value.type = JSON_TYPE.OBJECT;
|
||||||
value.object = null;
|
value.object = null;
|
||||||
|
|
||||||
if(testChar('}')) break;
|
if(testChar('}')) break;
|
||||||
|
|
||||||
do {
|
do
|
||||||
|
{
|
||||||
checkChar('"');
|
checkChar('"');
|
||||||
string name = parseString();
|
string name = parseString();
|
||||||
checkChar(':');
|
checkChar(':');
|
||||||
JSONValue member = void;
|
JSONValue member = void;
|
||||||
parseValue(&member);
|
parseValue(&member);
|
||||||
value.object[name] = member;
|
value.object[name] = member;
|
||||||
} while(testChar(','));
|
}
|
||||||
|
while(testChar(','));
|
||||||
|
|
||||||
checkChar('}');
|
checkChar('}');
|
||||||
break;
|
break;
|
||||||
|
@ -236,11 +259,13 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) {
|
||||||
|
|
||||||
if(testChar(']')) break;
|
if(testChar(']')) break;
|
||||||
|
|
||||||
do {
|
do
|
||||||
|
{
|
||||||
JSONValue element = void;
|
JSONValue element = void;
|
||||||
parseValue(&element);
|
parseValue(&element);
|
||||||
value.array ~= element;
|
value.array ~= element;
|
||||||
} while(testChar(','));
|
}
|
||||||
|
while(testChar(','));
|
||||||
|
|
||||||
checkChar(']');
|
checkChar(']');
|
||||||
break;
|
break;
|
||||||
|
@ -255,18 +280,21 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) {
|
||||||
auto number = appender!string();
|
auto number = appender!string();
|
||||||
bool isFloat, isNegative;
|
bool isFloat, isNegative;
|
||||||
|
|
||||||
void readInteger() {
|
void readInteger()
|
||||||
|
{
|
||||||
if(!isDigit(c)) error("Digit expected");
|
if(!isDigit(c)) error("Digit expected");
|
||||||
|
|
||||||
Next: number.put(c);
|
Next: number.put(c);
|
||||||
|
|
||||||
if(isDigit(peekChar())) {
|
if(isDigit(peekChar()))
|
||||||
|
{
|
||||||
c = getChar();
|
c = getChar();
|
||||||
goto Next;
|
goto Next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(c == '-') {
|
if(c == '-')
|
||||||
|
{
|
||||||
number.put('-');
|
number.put('-');
|
||||||
c = getChar();
|
c = getChar();
|
||||||
isNegative = true;
|
isNegative = true;
|
||||||
|
@ -274,13 +302,15 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) {
|
||||||
|
|
||||||
readInteger();
|
readInteger();
|
||||||
|
|
||||||
if(testChar('.')) {
|
if(testChar('.'))
|
||||||
|
{
|
||||||
isFloat = true;
|
isFloat = true;
|
||||||
number.put('.');
|
number.put('.');
|
||||||
c = getChar();
|
c = getChar();
|
||||||
readInteger();
|
readInteger();
|
||||||
}
|
}
|
||||||
if(testChar!(false, false)('e')) {
|
if(testChar!(false, false)('e'))
|
||||||
|
{
|
||||||
isFloat = true;
|
isFloat = true;
|
||||||
number.put('e');
|
number.put('e');
|
||||||
if(testChar('+')) number.put('+');
|
if(testChar('+')) number.put('+');
|
||||||
|
@ -290,11 +320,13 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
string data = number.data;
|
string data = number.data;
|
||||||
if(isFloat) {
|
if(isFloat)
|
||||||
|
{
|
||||||
value.type = JSON_TYPE.FLOAT;
|
value.type = JSON_TYPE.FLOAT;
|
||||||
value.floating = parse!real(data);
|
value.floating = parse!real(data);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
if (isNegative)
|
if (isNegative)
|
||||||
value.integer = parse!long(data);
|
value.integer = parse!long(data);
|
||||||
else
|
else
|
||||||
|
@ -342,14 +374,18 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) {
|
||||||
/**
|
/**
|
||||||
Takes a tree of JSON values and returns the serialized string.
|
Takes a tree of JSON values and returns the serialized string.
|
||||||
*/
|
*/
|
||||||
string toJSON(in JSONValue* root) {
|
string toJSON(in JSONValue* root)
|
||||||
|
{
|
||||||
auto json = appender!string();
|
auto json = appender!string();
|
||||||
|
|
||||||
void toString(string str) {
|
void toString(string str)
|
||||||
|
{
|
||||||
json.put('"');
|
json.put('"');
|
||||||
|
|
||||||
foreach (dchar c; str) {
|
foreach (dchar c; str)
|
||||||
switch(c) {
|
{
|
||||||
|
switch(c)
|
||||||
|
{
|
||||||
case '"': json.put("\\\""); break;
|
case '"': json.put("\\\""); break;
|
||||||
case '\\': json.put("\\\\"); break;
|
case '\\': json.put("\\\\"); break;
|
||||||
case '/': json.put("\\/"); break;
|
case '/': json.put("\\/"); break;
|
||||||
|
@ -367,12 +403,15 @@ string toJSON(in JSONValue* root) {
|
||||||
json.put('"');
|
json.put('"');
|
||||||
}
|
}
|
||||||
|
|
||||||
void toValue(in JSONValue* value) {
|
void toValue(in JSONValue* value)
|
||||||
final switch(value.type) {
|
{
|
||||||
|
final switch(value.type)
|
||||||
|
{
|
||||||
case JSON_TYPE.OBJECT:
|
case JSON_TYPE.OBJECT:
|
||||||
json.put('{');
|
json.put('{');
|
||||||
bool first = true;
|
bool first = true;
|
||||||
foreach(name, member; value.object) {
|
foreach(name, member; value.object)
|
||||||
|
{
|
||||||
if(first) first = false;
|
if(first) first = false;
|
||||||
else json.put(',');
|
else json.put(',');
|
||||||
toString(name);
|
toString(name);
|
||||||
|
@ -385,7 +424,8 @@ string toJSON(in JSONValue* root) {
|
||||||
case JSON_TYPE.ARRAY:
|
case JSON_TYPE.ARRAY:
|
||||||
json.put('[');
|
json.put('[');
|
||||||
auto length = value.array.length;
|
auto length = value.array.length;
|
||||||
foreach (i; 0 .. length) {
|
foreach (i; 0 .. length)
|
||||||
|
{
|
||||||
if(i) json.put(',');
|
if(i) json.put(',');
|
||||||
toValue(&value.array[i]);
|
toValue(&value.array[i]);
|
||||||
}
|
}
|
||||||
|
@ -436,8 +476,10 @@ private void appendJSONChar(Appender!string* dst, dchar c,
|
||||||
/**
|
/**
|
||||||
Exception thrown on JSON errors
|
Exception thrown on JSON errors
|
||||||
*/
|
*/
|
||||||
class JSONException : Exception {
|
class JSONException : Exception
|
||||||
this(string msg, int line = 0, int pos = 0) {
|
{
|
||||||
|
this(string msg, int line = 0, int pos = 0)
|
||||||
|
{
|
||||||
if(line) super(text(msg, " (Line ", line, ":", pos, ")"));
|
if(line) super(text(msg, " (Line ", line, ":", pos, ")"));
|
||||||
else super(msg);
|
else super(msg);
|
||||||
}
|
}
|
||||||
|
@ -450,7 +492,8 @@ version(unittest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unittest {
|
unittest
|
||||||
|
{
|
||||||
// An overly simple test suite, if it can parse a serializated string and
|
// An overly simple test suite, if it can parse a serializated string and
|
||||||
// then use the resulting values tree to generate an identical
|
// then use the resulting values tree to generate an identical
|
||||||
// serialization, both the decoder and encoder works.
|
// serialization, both the decoder and encoder works.
|
||||||
|
@ -478,13 +521,16 @@ unittest {
|
||||||
|
|
||||||
JSONValue val;
|
JSONValue val;
|
||||||
string result;
|
string result;
|
||||||
foreach(json; jsons) {
|
foreach(json; jsons)
|
||||||
try {
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
val = parseJSON(json);
|
val = parseJSON(json);
|
||||||
result = toJSON(&val);
|
result = toJSON(&val);
|
||||||
assert(result == json, text(result, " should be ", json));
|
assert(result == json, text(result, " should be ", json));
|
||||||
}
|
}
|
||||||
catch(JSONException e) {
|
catch(JSONException e)
|
||||||
|
{
|
||||||
writefln(text(json, "\n", e.toString()));
|
writefln(text(json, "\n", e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue