    package flare.data.converters
    {
        import com.adobe.serialization.json.JSON;

        import flare.data.DataField;
        import flare.data.DataSchema;
        import flare.data.DataSet;
        import flare.data.DataTable;
        import flare.data.DataUtil;
        import flare.util.Property;

        import flash.utils.ByteArray;
        import flash.utils.IDataInput;
        import flash.utils.IDataOutput;

        /**
         * Converts data between JSON (JavaScript Object Notation) strings and
         * flare DataSet instances.
         */
        public class JSONConverter implements IDataConverter
        {
            /**
             * @inheritDoc
             */
            public function read(input:IDataInput, schema:DataSchema=null):DataSet
            {
                var data:Array;
                return new DataSet(new DataTable(
                    data = parse(input.readUTFBytes(input.bytesAvailable), schema),
                    schema ? schema : DataUtil.inferSchema(data)
                ));
            }

            /**
             * Converts data from a JSON string into ActionScript objects.
             * @param input the loaded input data
             * @param schema a data schema describing the structure of the data.
             *  Schemas are optional in many but not all cases.
             * @param data an array in which to write the converted data objects.
             *  If this value is null, a new array will be created.
             * @return an array of converted data objects. If the <code>data</code>
             *  argument is non-null, it is returned.
             */
            public function parse(text:String, schema:DataSchema):Array
            {
                var json:Object = JSON.decode(text) as Object;
                var list:Array = json as Array;

                if (schema != null) {
                    if (schema.dataRoot) {
                        // if nested, extract data array
                        list = Property.$(schema.dataRoot).getValue(json);
                    }
                    // convert value types according to schema
                    for each (var t:Object in list) {
                        for each (var f:DataField in schema.fields) {
                            t[f.name] = DataUtil.parseValue(t[f.name], f.type);
                        }
                    }
                }
                return list;
            }

            /**
             * @inheritDoc
             */
            public function write(data:DataSet, output:IDataOutput=null):IDataOutput
            {
                var tuples:Array = data.nodes.data;
                if (output==null) output = new ByteArray();
                output.writeUTFBytes(JSON.encode(tuples));
                return output;
            }

        } // end of class JSONConverter
    }