8

I want to be able to parse a string to an object that I can access using the dot notation e.g. myobject.property, instead of the array notation e.g. myobject['property']. The array notation works fine. Here's what I have so far.

I have some XML:

<level1 name="level1name">
  <level2 type="level2Type">
    <entry>level2entry</entry>
    <entry>level2entry</entry>
  </level2>
</level1>

Which converts to the JSON:

{
  "level1": {
    "name": "level1name",
    "level2": {
      "type": "level2Type",
      "entry": [
        "level2entry",
        "level2entry"
      ]
    }
  }
}

I have the following Dart code:

Object jsonObject = JSON.parse("""{
      "level1": {
        "name": "level1name",
        "level2": {
          "type": "level2Type",
          "entry": [
            "level2entry",
            "level2entry"
          ]
        }
      }
    }
""");

  print("my test 1 == ${jsonObject}");
  print("my test 2 == ${jsonObject['level1']}");
  print("my test 3 == ${jsonObject['level1']['name']}");

which produce the (desired) output:

my test 1 == {level1: {name: level1name, level2: {type: level2Type, entry: [level2entry, level2entry]}}}
my test 2 == {name: level1name, level2: {type: level2Type, entry: [level2entry, level2entry]}}
my test 3 == level1name

But when I try:

print("my test 1 == ${jsonObject.level1}");

I get the following:

Exception: NoSuchMethodException : method not found: 'get:level1'
Receiver: {level1: {name: level1name, level2: {type: level2Type, entry: [level2entry, level2entry]}}}
Arguments: []
Stack Trace:  0. Function: 'Object.noSuchMethod' url: 'bootstrap' line:717 col:3

Ideally, I want an object that I can access using the dot notation and without the compiler giving warning about Object not having property. I tried the following:

class MyJSONObject extends Object{
  Level1 _level1;
  Level1 get level1() => _level1;
  set level1(Level1 s) => _level1 = s; 
}

class Level1 {
  String _name;
  String get name() => _name;
  set name(String s) => _name = s; 
}
...
MyJSONObject jsonObject = JSON.parse("""{
      "level1": {
        "name": "level1name",
        "level2": {
          "type": "level2Type",
          "entry": [
            "level2entry",
            "level2entry"
          ]
        }
      }
    }
""");
...
print("my test 1 == ${jsonObject.level1.name}");

but instead of giving me 'level1name' as hoped, I get:

Exception: type 'LinkedHashMapImplementation<String, Dynamic>' is not a subtype of type 'MyJSONObject' of 'jsonObject'.

What am I doing wrong here? Is there any way to do what I'm trying? Thanks.

1
  • This is kind of an aside; you ought to be able to convert from an object to javascript and from javascript to JSON. Commented Apr 29, 2014 at 22:51

2 Answers 2

12

At the moment, JSON.parse only returns Lists (array), Maps, String, num, bool, and null (api ref).

I suspect that until reflection makes it way into the language, it won't be able to re-construct objects based upon the keys found in json.

You could, however, create a constructor in your MyJsonObject which took a string, called JSON.parse internally, and assigned the various values.

Something like this works in the dart editor:

#import("dart:json");
class Level2 {
   var type;
   var entry;
}

class Level1 {
   var name;
   var level2;        
}

class MyJSONObject {
  Level1 level1;


  MyJSONObject(jsonString) {
     Map map = JSON.parse(jsonString);

     this.level1 = new Level1();
     level1.name = map['level1']['name'];
     level1.level2 = new Level2();
     level1.level2.type = map['level1']['level2']['type'];
     //etc...

  }
}

main() {
   var obj = new MyJSONObject(json);
   print(obj.level1.level2.type);

}

A non trivial version would needs some loops and possible recursion if you had deeper nested levels.

Update: I've hacked together a non-trivial version (inspired by the post below), it's up on github (also taking Seth's comments re the constructor):

Sign up to request clarification or add additional context in comments.

2 Comments

It's a shame about that language limitation. However, that was an outstanding answer. Exactly what I was looking for. Thanks Chris :)
I might suggest using a named constructor, something like MyJSONObject.fromJson(jsonString) or similar. This creates a more obvious intent for the constructor.
4

Chris is completely right. I will only add that the JSON parser could be modified to return a little richer object (something like JsonMap instead of pure Map) that could allow jsonObj.property by implementing noSuchMethod. That would obviously perform worse than jsonObj['property'].

1 Comment

Inspired by this comment, I've had a hack at that, and it's now up on github: github.com/chrisbu/dartwatch-JsonObject

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.