views:

183

answers:

7

I can't comprehend how it works without using eval - what's the secret behind this?

Edit: Could someone present a trivial example of how the tree structure is converted to an object?

+8  A: 

JSON has a well defined grammar which is used to construct a tree which is then converted to an object.

Darin Dimitrov
But how is the tree converted to the object?
meder
@Fedor - what you are asking is equivalent to "how is the JSON parser implemented"...
Yuval A
@Fedor - how is `eval` implemented?
orip
+2  A: 

I don't know specifics but it's not really hard. Just a smart combination of reading characters and substrings, interpreting what they mean and building up an array of data while you do it. Like any other parser.

Bart van Heukelom
+2  A: 

There is no secret. How do you think eval() is implemented? It uses the same techniques that you would have to use if you had to parse JSON data, i.e. effectively reimplement part of eval().

Kilian Foth
A: 

JSON is a native representation of data. It is simply a creative implementation of JavaScript's built-in object format. Being native, it doesn't need to be "parsed" (in the sense that the programmer needs to worry about doing it) at all.

Diodeus
What you're saying, eval() is a suitable parser for JSON. That's true, but that's not what the OP is about.
Seva Alekseyev
I never said that.
Diodeus
+3  A: 

Get Douglas Crockford's book, Javascript: the Good Parts. Appendix E includes code implementing a JSON parser. It does not use eval.

Nathan Hughes
+1  A: 

trivial example of how to convert a string with json into an object without eval:

  var txt='[\'one\',\'two\']';

  var re1='(\\[)';  // Any Single Character 1
  var re2='(\\\'.*?\\\')';  // Single Quote String 1
  var re3='(,)';    // Any Single Character 2
  var re4='(\\\'.*?\\\')';  // Single Quote String 2
  var re5='(\\])';  // Any Single Character 3

  var p = new RegExp(re1+re2+re3+re4+re5,["i"]);
  var m = p.exec(txt);
  if (m != null)
  {
      var c1=m[1];
      var s1=m[2];
      var c2=m[3];
      var s2=m[4];
      var c3=m[5];

      return [s1, s2];
  }
  return null;

yes this a horrible way to do it, but it does what it claims for that string :p

mkoryak
you know...technically your JSON string example isn't valid JSON. :-p
David Murdoch
i wasnt trying to be very correct here just give him the push in the right direction. also i was returning a hash not an array
mkoryak
+2  A: 

Take a look at my parser for a good idea. It isn't perfect but the code is pretty easy to follow.

public static JsonStructure Parse(string jsonText)
{
    var result = default(JsonStructure);
    var structureStack = new Stack<JsonStructure>();
    var keyStack = new Stack<string>();
    var current = default(JsonStructure);
    var currentState = ParserState.Begin;
    var key = default(string);
    var value = default(object);

    foreach (var token in Lexer.Tokenize(jsonText))
    {
        switch (currentState)
        {
            case ParserState.Begin:
                switch (token.Type)
                {
                    case TokenType.BeginObject:
                        currentState = ParserState.Name;
                        current = result = new JsonObject();
                        break;
                    case TokenType.BeginArray:
                        currentState = ParserState.Value;
                        current = result = new JsonArray();
                        break;
                    default:
                        throw new JsonException(token, currentState);
                }
                break;
            case ParserState.Name:
                switch (token.Type)
                {
                    case TokenType.String:
                        currentState = ParserState.NameSeparator;
                        key = (string)token.Value;
                        break;
                    default:
                        throw new JsonException(token, currentState);
                }
                break;
            case ParserState.NameSeparator:
                switch (token.Type)
                {
                    case TokenType.NameSeparator:
                        currentState = ParserState.Value;
                        break;
                    default:
                        throw new JsonException(token, currentState);
                }
                break;
            case ParserState.Value:
                switch (token.Type)
                {
                    case TokenType.Number:
                    case TokenType.String:
                    case TokenType.True:
                    case TokenType.False:
                    case TokenType.Null:
                        currentState = ParserState.ValueSeparator;
                        value = token.Value;
                        break;
                    case TokenType.BeginObject:
                        structureStack.Push(current);
                        keyStack.Push(key);
                        currentState = ParserState.Name;
                        current = new JsonObject();
                        break;
                    case TokenType.BeginArray:
                        structureStack.Push(current);
                        currentState = ParserState.Value;
                        current = new JsonArray();
                        break;
                    default:
                        throw new JsonException(token, currentState);
                }
                break;
            case ParserState.ValueSeparator:
                var jsonObject = (current as JsonObject);
                var jsonArray = (current as JsonArray);
                if (jsonObject != null)
                {
                    jsonObject.Add(key, value);
                    currentState = ParserState.Name;
                }
                if (jsonArray != null)
                {
                    jsonArray.Add(value);
                    currentState = ParserState.Value;
                }
                switch (token.Type)
                {
                    case TokenType.EndObject:
                    case TokenType.EndArray:
                        currentState = ParserState.End;
                        break;
                    case TokenType.ValueSeparator:
                        break;
                    default:
                        throw new JsonException(token, currentState);
                }
                break;
            case ParserState.End:
                switch (token.Type)
                {
                    case TokenType.EndObject:
                    case TokenType.EndArray:
                    case TokenType.ValueSeparator:
                        var previous = structureStack.Pop();
                        var previousJsonObject = (previous as JsonObject);
                        var previousJsonArray = (previous as JsonArray);
                        if (previousJsonObject != null)
                        {
                            previousJsonObject.Add(keyStack.Pop(), current);
                            currentState = ParserState.Name;
                        }
                        if (previousJsonArray != null)
                        {
                            previousJsonArray.Add(current);
                            currentState = ParserState.Value;
                        }
                        if (token.Type != TokenType.ValueSeparator)
                        {
                            currentState = ParserState.End;
                        }
                        current = previous;
                        break;
                    default:
                        throw new JsonException(token, currentState);
                }
                break;
            default:
                break;
        }
    }
    return result;
}
ChaosPandion