thriftpy2 icon indicating copy to clipboard operation
thriftpy2 copied to clipboard

Question about JSON protocol

Open wreed4 opened this issue 7 years ago • 10 comments

I'm trying to use the TJSONProtocolFactory with the client I'm generating. And from what I can tell, the JSON it is generating is not valid thrift json. For example for a given function call on the client, I'm getting something like this:

'{"metadata": {"version": 1, "name": "registerThing", "ttype": 1, "seqid": 0}, 
"payload": {"request": {"uuid": "12322312121", "url": "someUrl", "user": "JohnSmith", "password": "pass1234"}}}'

In contrast I think I would be expecting something closer to this (the last object part is almost definitely wrong, but that kind of format anyways):

[1, "registerThing, 1, 0, { "1": {"rec" : {"1": {"str": "12322312121"}, "2": {"str": "someUrl"...............

or something like that.

I am very new to Thrift, so I could be wrong here, but I'm confused as to how I'm supposed to use this Protocol. The server I'm trying to connect to is rejecting the request as thriftpy sends it (unfortunately I can't give too much detail on that as it is a proprietary system). I noticed this difference in the json by debugging the request from the client I'm trying to write and comparing it to the requests received from other clients written in other languages.

wreed4 avatar Oct 23 '18 15:10 wreed4

Could you please post your thrift IDL file here? I will try to reproduce it.

ethe avatar Oct 24 '18 03:10 ethe

Unfortunately, I can't paste the thrift file I'm using as it is proprietary. However, here is a very paired-down version of the specific endpoint/type i'm trying to use.

struct Foo {
  1: required string uuid
  2: required string url
  3: required string user
  4: required string password
}

struct FooRequest {
  1: required string uuid
  2: required string url
  3: required string user
  4: required string password
}

struct FooResponse {
  1: required Foo foo
}


service Api {
  FooResponse doFoo(1: FooRequest request)
}

The json it tries to send is

'{"metadata": {"version": 1, "name": "doFoo", "ttype": 1, "seqid": 0}, 
"payload": {"request": {"uuid": "12322312121", "url": "adjdafslkdfkljfad", "user": "23jk", "password": "j23"}}}'

This does not look like the format I expect for thrift.
I'm trying to use TMemoryBuffer (wrote my own factory) and TJSONProtocol.

wreed4 avatar Oct 24 '18 20:10 wreed4

Well, I tried TJSONProtocol of thriftpy just now, it seems incompatible with Apache Thrift. The request of method you posted above would be serialized to [1,"doFoo",1,0,{"1":{"rec":{"1":{"str":"1"},"2":{"str":"1"},"3":{"str":"sdf"},"4":{"str":"adsf"}}}}](uses TJSONProtocol) or {"request":{"uuid":"1","url":"1","user":"sdf","password":"adsf"}}(uses TSimpleJSONProtocol) with Apache Thrift. I do not know the reason of this incompatibility, But TBinaryProtocol is fully compatible with Apache Thrift. Maybe we can fix TJSONProtocol in the future, thanks!

ethe avatar Oct 25 '18 10:10 ethe

Is there a workaround for getting the correct protocol? I tried simply using the TJSONProtocol object from the official thrift module, but to no one's surprise that didn't work.

wreed4 avatar Oct 25 '18 15:10 wreed4

Use TBinaryProtocol rather than TJSONProtocol, if you want to use both Thriftpy and Apache Thrift at the same time.

ethe avatar Oct 26 '18 04:10 ethe

I don't think it's that so much. Rather, the server I'm trying to hit uses TJSONProtocol (it's set up really weird, but basically, it's not actually being sent with thrift, but it's deserializing thrift objects after the fact.. don't ask, it's enterprise software). So short of just rewriting the protocol, I'm not really sure what to do. Is there any way you know of to get around just rewriting the protocol from scratch if the server explicitly requires the TJSONProtocol format?

Sorry if that's a dumb question, I'm not particularly familiar with the thrift landscape.

wreed4 avatar Oct 26 '18 12:10 wreed4

There are two ways to solve your issue:

  1. make your custom protocol
  2. use Apache Thrift (TSimpleJSONProtocol or TJSONProtocol)

But, if you just want to deserialze JSON object to Python object, actually you do not need Thrift, there are tons of solutions to realize it:

from __future__ import print_function
import json

try:
    from types import SimpleNamespace as Namespace
except ImportError:
    # Python 2.x fallback
    from argparse import Namespace

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

x = json.loads(data, object_hook=lambda d: Namespace(**d))

print (x.name, x.hometown.name, x.hometown.id)

ethe avatar Oct 28 '18 04:10 ethe

That's not my usecase, no. I need to communicate with an xmlrpc server that only accepts TJSONProtocol-style json. So I need to go from a thrift python object, to TJSONProtocol json format, send that to the server, then read TJSONProtocol json back into a thrift python object. The only parts thriftpy doesn't give me apparently is the TJSONProtocol part.

I've tried adapting the Apache Thrift TJSONProtocol object to be able to be used with thriftpy, but that seems like not worth the effort. Currently I'm trying to overwrite enough of thriftpy.TJSONProtocol and surrounding modules that it gives me the correct json. But that's looking more and more like I'd just need to rewrite/adapt most of the protocol since it doesn't seem like TJSONProtocol is all that extensible.

Any ideas you have that are easier are definitely appreciated.

wreed4 avatar Oct 29 '18 18:10 wreed4

You need to overwrite TJSONProtocol, Apache Thrift TJSONProtocol can not be used with thriftpy.

ethe avatar Oct 30 '18 07:10 ethe

I've implemented a JSONProtocol that implements the apache thrift version which also supports binary data (see my fork for the change: https://github.com/Thriftpy/thriftpy2/pull/139

I might not have covered all cases and with python2, but it works in my codebase.

JonnoFTW avatar Oct 01 '20 02:10 JonnoFTW