Parsing json, key without quotes
Update for Swift 4
I was looking for a way to parse JSON that has keys without quotes, and I finally found a simple way to do it by using regex. This is the regex needed to match the keys without quotes:
(\\\"(.*?)\\\"|(\\w+))(\\s*:\\s*(\\\".*?\\\"|.))
To add the quotes, replace with \"$2$3\"$4
.
Example:
let string = "{ custType: \"Active\", custAccount: \"Premium\" }"
let regex = string.replacingOccurrences(of: "(\\\"(.*?)\\\"|(\\w+))(\\s*:\\s*(\\\".*?\\\"|.))", with: "\"$2$3\"$4", options: .regularExpression)
print(regex)
Output:
{ "custType": "Active", "custAccount": "Premium" }
I originally put this in as a comment, but I think it counts as an answer albeit not necessarily a very helpful one.
The example you posted is not JSON. Check the JSON syntax. The only unquoted entities allowed except for numbers, objects and arrays are null
, true
, false
. So the keys in your example are invalid and so are the non numeric values.
So you really should raise a defect report with the service provider (if they are claiming that they are producing JSON, rather than some modified version of it).
If they refuse to fix the problem, you'll need to write a nearly-JSON parser or find an existing one that is less strict about syntax.
I wanted the equivalent of Python's ast.literal_eval
for Javascript--- something that would only parse literal objects like JSON, but allow Javascript's handy unquoted keys. (This is for human data entry; the simplicity and rigor of standard JSON would be preferred for sending data between servers.)
This is also what the original poster wanted, so I'll put my solution here. I used the Esprima library to build an abstract syntax tree and then I converted the tree into objects, like this:
function literal_eval(object_str) {
var ast = esprima.parse("var dummy = " + object_str);
if (ast.body.length != 1 || ast.body[0].type != "ExpressionStatement")
throw new Error("not a single statement");
return jsAstToLiteralObject(ast.body[0].expression.right);
}
function jsAstToLiteralObject(ast) {
if (ast.type == "Literal")
return ast.value;
else if (ast.type == "ArrayExpression") {
var out = [];
for (var i in ast.elements)
out.push(jsAstToLiteralObject(ast.elements[i]));
return out;
}
else if (ast.type == "ObjectExpression") {
var out = {};
for (var k in ast.properties) {
var key;
if (ast.properties[k].type == "Property" &&
ast.properties[k].key.type == "Literal" &&
typeof ast.properties[k].key.value == "string")
key = ast.properties[k].key.value;
else if (ast.properties[k].type == "Property" &&
ast.properties[k].key.type == "Identifier")
key = ast.properties[k].key.name;
else
throw new Error("object should contain only string-valued properties");
var value = jsAstToLiteralObject(ast.properties[k].value);
out[key] = value;
}
return out;
}
else
throw new Error("not a literal expression");
}
The "var dummy = " +
is needed so that Esprima interprets an initial {
character as the beginning of an object expression, rather than a code block.
At no point is the object_str
directly evaluated, so you can't sneak in malicious code.
As a side benefit, users can also include comments in the object_str
.
For this kind of problem, YAML is also worth considering. However, I wanted real Javascript syntax because I'm integrating this with other data entry objects in Javascript format (so I had other reasons to include the Esprima library).