# -*- coding: utf-8 -*- # # python-json-pointer - An implementation of the JSON Pointer syntax # https://github.com/stefankoegl/python-json-pointer # # Copyright (c) 2011 Stefan Kögl # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # """ Identify specific nodes in a JSON document according to http://tools.ietf.org/html/draft-pbryan-zyp-json-pointer-00 """ # Will be parsed by setup.py to determine package metadata __author__ = 'Stefan Kögl ' __version__ = '0.1' __website__ = 'https://github.com/stefankoegl/python-json-pointer' __license__ = 'Modified BSD License' import urllib class JsonPointerException(Exception): pass _nothing = object() def resolve_pointer(doc, pointer, default=_nothing): """ Resolves pointer against doc and returns the referenced object >>> obj = {"foo": {"anArray": [ {"prop": 44}], "another prop": {"baz": "A string" }}} >>> resolve_pointer(obj, '/') == obj True >>> resolve_pointer(obj, '/foo') == obj['foo'] True >>> resolve_pointer(obj, '/foo/another%20prop') == obj['foo']['another prop'] True >>> resolve_pointer(obj, '/foo/another%20prop/baz') == obj['foo']['another prop']['baz'] True >>> resolve_pointer(obj, '/foo/anArray/0') == obj['foo']['anArray'][0] True >>> resolve_pointer(obj, '/some/path', None) == None True """ pointer = JsonPointer(pointer) return pointer.resolve(doc, default) class JsonPointer(object): """ A JSON Pointer that can reference parts of an JSON document """ def __init__(self, pointer): parts = pointer.split('/') if parts.pop(0) != '': raise JsonPointerException('location must starts with /') self.parts = map(urllib.unquote, parts) def resolve(self, doc, default=_nothing): """Resolves the pointer against doc and returns the referenced object""" for part in self.parts: try: doc = self.walk(doc, part) except JsonPointerException: if default is _nothing: raise else: return default return doc get = resolve def walk(self, doc, part): """ Walks one step in doc and returns the referenced part """ if not part: return doc # Its not clear if a location "1" should be considered as 1 or "1" # We prefer the integer-variant if possible part_variants = self._try_parse(part) + [part] for variant in part_variants: try: return doc[variant] except: continue raise JsonPointerException("'%s' not found in %s" % (part, doc)) @staticmethod def _try_parse(val, cls=int): try: return [cls(val)] except: return []