Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing JSON with Dart

Tags:

json

dart

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.

like image 330
Phlox Midas Avatar asked Apr 05 '12 15:04

Phlox Midas


2 Answers

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):

like image 134
Chris Buckett Avatar answered Nov 07 '22 16:11

Chris Buckett


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'].

like image 30
Ladicek Avatar answered Nov 07 '22 17:11

Ladicek