3

How does one return a dict like object through protoRPC ?

I tried using the FieldList to no avail. I only see the following field definitions:

       'IntegerField',
       'FloatField',
       'BooleanField',
       'BytesField',
       'StringField',
       'MessageField',
       'EnumField',
jldupont
  • 93,734
  • 56
  • 203
  • 318

4 Answers4

9

There are two scenarios:

1) Your dict has a well-defined schema: This is the best use case for ProtoRPC and if possible you should try to fit it into a schema. In this case, you would use a MessageField with some Message class that matches the schema in your dictionary.

For example, instead of

{'amount': 31, 'type': 'fish', mine: False}

you could define

from protorpc import messages
class MyCatch(messages.Message):
  amount = messages.IntegerField(1)
  type = messages.StringField(2)
  mine = messages.BooleanField(3)

and then use this message definition in a field via

messages.MessageField(MyCatch, index, ...)

2) Your dict does not have a well-defined schema: In this case you can use json to dump your dictionary to a string and request ensure_ascii=True to make sure the return type is a bytes (str) object. Then you can just use a BytesField.

For example:

import json

class MyMessage(messages.Message):
   some_dict = messages.BytesField(1)

my_dict = {'amount': 31, 'type': 'fish', mine: False}
message = MyMessage(some_dict=json.dumps(my_dict, ensure_ascii=True))

The use of ensure_ascii is optional as True is the default, but this may change depending on your environment.

Instead you could use pickle to serialize your dictionary. The method pickle.dumps always outputs ASCII/binary, so by swapping out json.dumps for pickle.dumps and dropping the ensure_ascii=True, you'd have the same outcome.

bossylobster
  • 9,993
  • 1
  • 42
  • 61
  • 1
    has something changed in the API? When I do this, I get a base64 encoded string on the client side. When decode the string I get the JSON back. Any idea why? – vivekv Jul 30 '13 at 16:04
  • Are you using `messages.BytesField`? – bossylobster Jul 30 '13 at 20:51
  • Yes I am @bossylobster. Here is my code snippet. Definition return CompanyConfigResponse( status = 'OK', name = cmp.name, barcodetype=cmp.barcodetype, configs = json.dumps(cinfo,ensure_ascii=True)) – vivekv Jul 31 '13 at 00:05
  • Here is my definition configs = messages.BytesField(5, required=False) – vivekv Jul 31 '13 at 00:11
  • Yes, that is the expected behavior of a `BytesField`. How else would you websafe encode bytes? – bossylobster Jul 31 '13 at 00:53
3

It's possible to create a custom JsonField like this :

In  [1]: class JsonField(messages.StringField):
             type = dict

You can then use it as any other field :

In  [2]: class MyMessage(messages.Message):
             data = JsonField(1)

In  [3]: m = MyMessage(data={"foo": "bar"})

In  [4]: m.data
Out [4]: {'foo': 'bar'}
volent
  • 473
  • 6
  • 16
2

For the first option in the approved answer, we can add a parameter repeated=True, so we'll have a list of json as the answer. I checked about it at https://developers.google.com/appengine/docs/python/tools/protorpc/overview?hl=en#Defining_the_Response_Message

0

A bit involved, but I have a recipe for something quite close to a dict implementation for protorpc: https://gist.github.com/linuxluser/32d4a9c36ca0b8715ad4

It is restricted to using string-only keys and simple (not nested) values. But if your data fits in that category, this solution should work well.

The idea has 2 parts:

  1. Create a new field type MultiField that can hold an arbitrary value type.
  2. Create a dict-like type MapField that stores key-value pairs in a list of MultiField types.

You use it like so:

import messages
import mapfield

class MyMessage(messages.Message):
  some_dict = mapfield.MapField(1)


my_message = MyMessage(some_dict={"foo": 7, "bar": False, "baz": 9.2, "qux": "nog"})

It's only a start. Probably could be better. Improvements are welcomed. :)

Dave
  • 3,428
  • 30
  • 28