1
class MSG_TYPE(IntEnum): REQUEST = 0 GRANT = 1 RELEASE = 2 FAIL = 3 INQUIRE = 4 YIELD = 5 def __json__(self): return str(self) class MessageEncoder(JSONEncoder): def default(self, obj): return obj.__json__() class Message(object): def __init__(self, msg_type, src, dest, data): self.msg_type = msg_type self.src = src self.dest = dest self.data = data def __json__(self): return dict (\ msg_type=self.msg_type, \ src=self.src, \ dest=self.dest, \ data=self.data,\ ) def ToJSON(self): return json.dumps(self, cls=MessageEncoder) msg = Message(msg_type=MSG_TYPE.FAIL, src=0, dest=1, data="hello world") encoded_msg = msg.ToJSON() decoded_msg = yaml.load(encoded_msg) print type(decoded_msg['msg_type']) 

When calling print type(decoded_msg['msg_type']), I get the result <type 'str'> instead of the original MSG_TYPTE type. I feel like I should also write a custom json decoder but kind of confused how to do that. Any ideas? Thanks.

1
  • 1
    btw, you don't need \ after , Commented Apr 17, 2015 at 18:55

2 Answers 2

6

When calling print type(decoded_msg['msg_type']), I get the result instead of the original MSG_TYPTE type.

Well, yeah, that's because you told MSG_TYPE to encode itself like this:

def __json__(self): return str(self) 

So, that's obviously going to decode back to a string. If you don't want that, come up with some unique way to encode the values, instead of just encoding their string representations.

The most common way to do this is to encode all of your custom types (including your enum types) using some specialized form of object—just like you've done for Message. For example, you might put a py-type field in the object which encodes the type of your object, and then the meanings of the other fields all depend on the type. Ideally you'll want to abstract out the commonalities instead of hardcoding the same thing 100 times, of course.


I feel like I should also write a custom json decoder but kind of confused how to do that.

Well, have you read the documentation? Where exactly are you confused? You're not going to get a complete tutorial by tacking on a followup to a StackOverflow question…

Assuming you've got a special object structure for all your types, you can use an object_hook to decode the values back to the originals. For example, as a quick hack:

class MessageEncoder(JSONEncoder): def default(self, obj): return {'py-type': type(obj).__name__, 'value': obj.__json__()} class MessageDecoder(JSONDecoder): def __init__(self, hook=None, *args, **kwargs): if hook is None: hook = self.hook return super().__init__(hook, *args, **kwargs) def hook(self, obj): if isinstance(obj, dict): pytype = obj.get('py-type') if pytype: t = globals()[pytype] return t.__unjson__(**obj['value']) return obj 

And now, in your Message class:

@classmethod def __unjson__(cls, msg_type, src, dest, data): return cls(msg_type, src, dest, data) 

And you need a MSG_TYPE.__json__ that returns a dict, maybe just {'name': str(self)}, then an __unjson__ that does something like getattr(cls, name).

A real-life solution should probably either have the classes register themselves instead of looking them up by name, or should handle looking them up by qualified name instead of just going to globals(). And you may want to let things encode to something other than object—or, if not, to just cram py-type into the object instead of wrapping it in another one. And there may be other ways to make the JSON more compact and/or readable. And a little bit of error handling would be nice. And so on.


You may want to look at the implementation of jsonpickle—not because you want to do the exact same thing it does, but to see how it hooks up all the pieces.

Sign up to request clarification or add additional context in comments.

Comments

-1

Overriding the default method of the encoder won't matter in this case because your object never gets passed to the method. It's treated as an int.

If you run the encoder on its own:

msg_type = MSG_TYPE.RELEASE MessageEncoder().encode(msg_type) 

You'll get:

'MSG_TYPE.RELEASE' 

If you can, use an Enum and you shouldn't have any issues. I also asked a similar question:

How do I serialize IntEnum from enum34 to json in python?

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.