Changeset 21

Show
Ignore:
Timestamp:
02/28/07 14:40:22 (2 years ago)
Author:
kevin
Message:

add support for complex objects via JSON

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/tgwebservices/controllers.py

    r20 r21  
    1414import cElementTree as et 
    1515from genshi.builder import tag, Element 
     16import simplejson 
    1617 
    1718from turbogears import controllers, expose as tgexpose 
     
    161162    return instance 
    162163 
    163 class ComplexConverter(object): 
    164     pass_xml = True 
    165      
    166     def __init__(self, cls): 
    167         self.cls = cls 
    168      
    169     def __call__(self, val): 
    170         return _get_single_value(val, self.cls) 
    171  
    172164def _handle_xml_params(body, input_types): 
    173165    kw = {} 
     
    204196                % (key, input_types.keys()), key, None 
    205197            ) 
    206              
     198 
     199def _get_json_value(value, itype): 
     200    if isinstance(itype, list): 
     201        itemtype = itype[0] 
     202        return [_get_json_value(item, itemtype) for item in value] 
     203    elif not isinstance(itype, type): 
     204        return _get_json_value(value, type(itype)) 
     205    elif itype not in primitives and isinstance(value, dict): 
     206        return _create_instance_from_json(value, itype) 
     207    return itype(value) 
     208 
     209def _create_instance_from_json(value, cls): 
     210    instance = cls() 
     211    for key in value: 
     212        try: 
     213            itype = getattr(cls, key) 
     214        except AttributeError: 
     215            raise validators.Invalid("%s is an unknown parameter for a %s"  
     216                                     % (key, cls.__name__), 
     217                key, None) 
     218        setattr(instance, str(key), _get_json_value(value[key], itype)) 
     219    return instance 
     220 
     221def _handle_json_params(input, input_types): 
     222    if isinstance(input, basestring): 
     223        try: 
     224            input = simplejson.loads(input) 
     225        except ValueError: 
     226            raise validators.Invalid("Invalid JSON input") 
     227    kw = {} 
     228    for key in input: 
     229        if key in input_types: 
     230            kw[str(key)] = _get_json_value(input[key], input_types[key]) 
     231        else: 
     232            raise validators.Invalid( 
     233                "%s is not a valid parameter (valid values are: %s)" 
     234                % (key, input_types.keys()), key, None 
     235            ) 
     236    return kw 
     237         
     238 
    207239def wsvalidate(*args, **kw): 
    208240    """Validates and converts incoming parameters. Also registers the  
     
    240272                                             None) 
    241273                kw = _handle_xml_params(body, input_types) 
     274            elif request.headers.get("Content-Type", "") \ 
     275                .startswith("application/json"): 
     276                clen = int(request.headers.get('Content-Length')) or 0 
     277                data = request.body.read(clen) 
     278                kw = _handle_json_params(data, input_types) 
    242279            else: 
    243280                _handle_keyword_params(kw, input_types) 
  • trunk/tgwebservices/tests/fixtures.py

    r20 r21  
    9292    def tenyearsolder(self, person): 
    9393        person.age = person.age + 10 
     94        self.last_person = person 
    9495        return person 
    9596 
  • trunk/tgwebservices/tests/test_controllers.py

    r19 r21  
    11import cElementTree as et 
    22 
    3 from tgwebservices.controllers import ComplexConverter 
     3from tgwebservices.controllers import _get_single_value, _handle_json_params 
    44 
    55class Person(object): 
     
    1414    <married>no</married> 
    1515</p>""") 
    16     cc = ComplexConverter(Person) 
    17     val = cc(tree) 
     16    val = _get_single_value(tree, Person) 
    1817    print val 
    1918    assert isinstance(val, Person) 
     
    4241</family> 
    4342""") 
    44     cc = ComplexConverter(Family1) 
    45     val = cc(tree) 
     43    val = _get_single_value(tree, Family1) 
    4644    assert val.lastname == "Brady" 
    4745    print val.members 
     
    9290</family> 
    9391""") 
    94     cc = ComplexConverter(Family2) 
    95     val = cc(tree) 
     92    val = _get_single_value(tree, Family2) 
    9693    assert val.lastname == "Brady" 
    9794    print val.members 
     
    106103    <item>true</item> 
    107104</list>""") 
    108     cc = ComplexConverter([bool]) 
    109     val = cc(tree) 
     105    val = _get_single_value(tree, [bool]) 
    110106    print val 
    111107    assert len(val) == 5 
     108 
     109def test_json_conversion_of_class(): 
     110    input_types = {"person" : Person} 
     111    data = {"person" : {"name" : "Fred", "age" : 99}} 
     112    kw = _handle_json_params(data, input_types) 
     113    assert isinstance(kw["person"], Person) 
     114    print kw["person"].age 
     115    assert kw["person"].age == 99 
     116    assert kw["person"].name == "Fred" 
     117 
     118def test_json_conversion_of_nested_class(): 
     119    input_types = {"family" : Family2} 
     120    data = {"family" : {"lastname" : "Brady", "members" : 
     121        [{"name" : "Mike", "age" : 42}, {"name" : "Carol", "age" : 40}] 
     122}} 
     123    kw = _handle_json_params(data, input_types) 
     124    assert isinstance(kw["family"], Family2) 
     125    assert len(kw["family"].members) == 2 
     126    assert kw["family"].members[0].name == "Mike" 
     127    assert kw["family"].members[0].age == 42 
  • trunk/tgwebservices/tests/test_json.py

    r15 r21  
     1import cStringIO as StringIO 
     2 
    13import cherrypy 
    24from turbogears import testutil 
     
    6466    for test in tests: 
    6567        yield check, test[0], test[1] 
    66          
     68 
     69def test_complex_input(): 
     70    cherrypy.root = ComplexService("http://foo.bar.baz/") 
     71     
     72    request = """{ 
     73        "person" : {"name" : "Fred", "age" : 22} 
     74}""" 
     75    testutil.create_request("/tenyearsolder", rfile=StringIO.StringIO(request),  
     76                            method="POST",  
     77                            headers={"Content-Length" : str(len(request)), 
     78                                     "Content-Type" :  
     79                                        "application/json; charset=utf-8"}) 
     80    output = cherrypy.response.body[0] 
     81    print output 
     82    print cherrypy.root.last_person.age 
     83    assert cherrypy.root.last_person.age == 32