JSONEncoder for rdflib.term.Literal et al.

55 views
Skip to first unread message

Donny Winston

unread,
Oct 15, 2021, 2:19:10 PM10/15/21
to rdfli...@googlegroups.com

Hi all,

Perhaps this is hidden somewhere in the rdflib repo and I wasn’t able to find it, but I found myself wanting to encode rdflib.term.Literal values to JSON as e.g. {“value”: literal.value, “datatype”: literal.datatype, …}. I ended up adapting the advice of a SO post. Here is my answer on SO with an rdflib example: [https://stackoverflow.com/a/69588931/1820042](https://stackoverflow.com/a/69588931/1820042).

Also reproduced below. I hope folks find it useful. Also feel free to point out where this has already been done, and I’ll pip install that instead of my custom code!

from json.encoder import (_make_iterencode, JSONEncoder,
                          encode_basestring_ascii, INFINITY,
                          encode_basestring)

class CustomObjectEncoder(JSONEncoder):

    def iterencode(self, o, _one_shot=False):
        """Encode the given object and yield each string
        representation as available.

        For example::

            for chunk in JSONEncoder().iterencode(bigobject):
                mysocket.write(chunk)

        Change from json.encoder.JSONEncoder.iterencode is setting
        _one_shot=False and isinstance=self.isinstance
        in call to `_make_iterencode`.
        And not using `c_make_encoder`.

        """
        if self.check_circular:
            markers = {}
        else:
            markers = None
        if self.ensure_ascii:
            _encoder = encode_basestring_ascii
        else:
            _encoder = encode_basestring

        def floatstr(o, allow_nan=self.allow_nan,
                _repr=float.__repr__, _inf=INFINITY, _neginf=-INFINITY):
            # Check for specials.  Note that this type of test is processor
            # and/or platform-specific, so do tests which don't depend on the
            # internals.

            if o != o:
                text = 'NaN'
            elif o == _inf:
                text = 'Infinity'
            elif o == _neginf:
                text = '-Infinity'
            else:
                return _repr(o)

            if not allow_nan:
                raise ValueError(
                    "Out of range float values are not JSON compliant: " +
                    repr(o))

            return text

        _iterencode = _make_iterencode(
                markers, self.default, _encoder, self.indent, floatstr,
                self.key_separator, self.item_separator, self.sort_keys,
                self.skipkeys, _one_shot=False, isinstance=self.isinstance)
        return _iterencode(o, 0)

Example subclass:

import datetime

from rdflib.term import Literal, BNode

class RDFTermEncoder(CustomObjectEncoder):
    def isinstance(self, o, cls):
        if isinstance(o, (Literal, BNode)):
            return False
        return isinstance(o, cls)
    def default(self, o):
        if isinstance(o, Literal):
            rv = {"value": o.value}
            if o.datatype is not None:
                rv["datatype"] = o.datatype
            if o.language is not None:
                rv["lang"] = o.language
            return rv
        if isinstance(o, BNode):
            return "http://localhost/bnode/" + str(o)
        if isinstance(o, datetime.datetime):
            return o.isoformat()
        if isinstance(o, datetime.date):
            return str(o)
        # Let the base class default method raise the TypeError
        return super().default(o)

I just used this successfully for my work as

db_json = json.loads(json.dumps(db_custom, cls=RDFTermEncoder))

Best,
Donny


Donny Winston, PhD (he/him/his) | Polyneme LLC
https://donnywinston.com | https://polyneme.xyz

If I’ve emailed you, I’d love to speak with you.
Schedule a meeting (15min+): https://meet.polyneme.xyz

Niklas Lindström

unread,
Oct 15, 2021, 3:43:58 PM10/15/21
to rdfli...@googlegroups.com

--
http://github.com/RDFLib
---
You received this message because you are subscribed to the Google Groups "rdflib-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rdflib-dev+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/rdflib-dev/5a70d567-652c-45da-b500-7776447210dd%40www.fastmail.com.

Donny Winston

unread,
Oct 15, 2021, 5:59:50 PM10/15/21
to rdfli...@googlegroups.com
Hmm. I knew about JSON-LD, but I was wanting to build up otherwise-json-serializable python objects that happened to contain rdflib.term objects, and serialize that to json. I suppose that is playing on hard mode though, and I'm better off building up rdflib graphs of what I need, serializing them to json-ld, and then picking at them from that point.

Thanks,
Donny
Reply all
Reply to author
Forward
0 new messages