Skip to content

JSONField does not convert to/from string #8011

@powelleric

Description

@powelleric

I am inquiring about the behavior of JSONField and specifically about the code here:
https://github.com/encode/django-rest-framework/blob/master/rest_framework/fields.py#L1788-L1804

My expectation for JSONField is that it will take a JSON object and convert it to a string to be stored in the database. In the other direction, it will get the string from the database and convert it to JSON for the response. However, this does not seem to be what is happening.

Looking at the relevant code in to_internal_value, I don't understand why the result of line 1795 (json.dumps) is not returned. That means we fall back to return data on line 1798, which (in the non-binary case) is just the unaltered value. This (python object) is then converted to a string and stored in the DB. However, the string is not json, as it has single quotes around property names. (I can't comment on the binary handling as I don't have experience working with that format.)

Second, I find that to_representation is not returning json. And, in fact, what is stored in the database is invalid for json.loads due to the single quotes.

In my own project, I resolved these issues with the following overrides. I can say that this definitely handles my non-binary use case as I desire but I did not really know how to test the binary handling.

 class JSONNativeField(serializers.JSONField):

     def to_internal_value(self, data):
         if (
            (self.binary or getattr(data, 'is_json_string', False))
             and isinstance(data, bytes)
         ):
             data = data.decode()
         try:
             return json.dumps(data, cls=self.encoder)
         except (TypeError, ValueError):
             self.fail('invalid')

     def to_representation(self, value):
         value = json.loads(value, cls=self.decoder)
         if self.binary:
             value = value.encode()
         return value

My implementation also has the advantage that the POST & GET formats are consistent. This is not the case currently: if I create a new object, the response contains the json field in json format. However, if I perform a get on that same newly-created object, the response contains a string for the json field and it contains single-quoted property names. I think that regardless of the behavior, the response format should be consistent between POST/GET.

Finally, note this SO answer where the person performs their own json.loads in to_representation for their serializer. I would say this person is experiencing the same challenges with JSONField as myself.
https://stackoverflow.com/a/54311320/2117491

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions