allow a json root attribute that is a container for object attributes

13 views
Skip to first unread message

brandon logan

unread,
Sep 9, 2014, 3:44:51 PM9/9/14
to pytho...@googlegroups.com
I can't find any place it looks like this is able to be accomplished so I'll give some examples and possible solutions just in case it is not already solved for.  If it has please let me know.

Assuming I have this complex type:

class Container(types.Base):
    name
= types.text

when WSME serializes to json it produces:

{"name": null}


what I would like is for WSME to serialize to:

{"container": {"name": null}}

Where "container" could be derived from either the class name or some overrideable attribute in the class like:

class Container(types.Base):
    _json_root
= 'container'
    name
= types.text




This should also result in the json root tag not being rendered when these complex types are in a list, but a plural version should be added as the root tag of the list.  Example:

{"containers": [{"name": null}]}


NOT this (or it can be customized)

{"containers": [{"container": {"name": null}}]


This of course means there should be a plural version of the json root defined because adding an 's' doesn't always work:

class Container(types.Base):
    _json_root = 'container'
    _json_root_list = 'containers'
    name 
= types.text






Christophe de Vienne

unread,
Sep 9, 2014, 4:45:38 PM9/9/14
to pytho...@googlegroups.com
Hello Brandon,

Let me rephrase your need so you can check I understand correctly.

Given a complex type, for example :

    class Person(types.base):
        name = types.text

you want to be able to transmit either a single instance, either a list of instance.

So that:

    Person(name='Joseph')

is serialized to

    {"person": {"name": "Joseph"}}
 
And

    [Person(name='Joseph')]

to

    {"persons": [{"name": "Joseph"}]}


If this is indeed what you intend to do, it is not possible exactly this way with WSME.

The closest thing to that you can do in WSME is the following, and it is almost what you ask for.

You can define a new container type for Person, that can hold a single instance on an attribute, and a list the another.
The only difference with what you want is that on the python code, you will have to manipulate this extra type. The json will, on the other hand, be exactly what you expect.

A bit of code :

    class Person(types.Base):
        name = types.text

    class PersonHolder(types.base):
        person = Person
        people = [Person]

        def __init__(self, value):
            if isinstance(value, list):
                kw = {'people': value}
            else:
                kw = {'person': value}
            super(PersonHolder, self).__init__(**kw)

        @property
        def value(self):
            assert Unset in (self.person, self.people)
            if self.person is not Unset:
                return self.person
            else:
                return self.people

With these types, the json serialization will be:

    PersonHolder(Person(name="Joseph")) -> {"person": {"name": "Joseph"}}
    PersonHolder([Person(name="Joseph")]) -> {"people": [{"name": "Joseph"}]}

As an input type, the holder makes it easier to check the actual type that was sent:

    @expose(None, PersonHolder)
    def work_w_person_or_people(holder):
        if holder.person is not Unset:
            # working with a single instance
        elif holder.people is not Unset:
            # working with a list

        # ... or work directly with holder.value

Note that this technique can be used when some attribute or parameter can have different types generally speaking.

Best regards,

Christophe
--
You received this message because you are subscribed to the Google Groups "python-wsme" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python-wsme...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

brandon logan

unread,
Sep 10, 2014, 3:03:42 PM9/10/14
to pytho...@googlegroups.com
Hi Christophe,

Thanks for your response and yes that is exactly what I want.  I've tried it out and it does work.  I did have to change the init method of the Holder object to make value optional because when using it as input the default fromjson fails because it is instantiating it without args.

I do feel this is a bit hackish.  It'll work for now, but do you think it would be something I could write a patch for WSME and submit it using the solution I described above?

Thanks,
Brandon

Christophe de Vienne

unread,
Sep 10, 2014, 3:57:27 PM9/10/14
to pytho...@googlegroups.com
Hi Brandon,

I do not think it is hackish, but it is a matter of taste. Would it feel less hackish if WSME provided a helper to build such a holder ? like :

    variant(person=Person, people=[Person]) -> holder type

This could be implemented easily. What could also be useful at some point is a truly generic container that could carry any type defined in an api. But we would need to make it strict-protocols friendly (it is doable).

Your proposal on the other hand... well, I do not like it much. It solves a very specific case (yours), and is limited to list and single instance. Also, it consider only the json encoding (although it could be a little more generic). And most of all, what if we want only single instance for some attributes, and single or list for others ? My solution allow to use the holder type only when we need it.

Note that it is my personal opinion, I would welcome any contradiction.

Cheers,

Christophe

brandon logan

unread,
Sep 10, 2014, 7:56:46 PM9/10/14
to pytho...@googlegroups.com
The reason I say it's hackish is because when used with the @expose(ReturnType, InputType), it doesn't seem intuitive to have a Holder be a return type if in fact the real return type is what the Holder is holding.  Does that make sense?  It probably is a matter of taste though.  I see the {"container": {"name": null}} as one single object, whereas you probably see it as a nested object, which is totally understandable and probably makes more sense than my distorted perspective.  Hmm, maybe I just convinced myself that you are right.  How did you do that?

Anyway, I'll concede this battle.  On to the next one!
Reply all
Reply to author
Forward
0 new messages