Package: jsonapi-transformer
jsonapi-transformerjsonapi-transformeris a Python library for producing, consuming, and manipulating JSON:API data. It makes developing with JSON:API more manageable by converting from and to JSON:API-formatted data.This library follows theJSON:API v1.1release candidatespecification.Quick StartThe following example is based onExample 7.2.2.4of the JSON:API v1.1 specification.importjsonfromtransformersimportJSONAPITransformer,from_jsonapi_genericData can be manually set on aJSONAPITransformerinstance...transformer=JSONAPITransformer(type_name="articles",id="1",attributes={"title":"Rails is Omakase",},relationships={"author":JSONAPITransformer(type_name="people",id="9",)})... and then converted to jsonapi.>>>jsonapi=transformer.to_jsonapi()>>>print(json.dumps(jsonapi,indent=4)){"data":{"type":"articles","id":"1","attributes":{"title":"Rails is Omakase"},"relationships":{"author":{"data":{"type":"people","id":"9"}}}}}If you already have jsonapi data, you can load that into aJSONAPITransformerinstance as well.>>>transformer=from_jsonapi_generic(jsonapi)>>>print(transformer.id)1>>>print(transformer.type_name)articlesInstallationjsonapi-transformeris available on PyPI.pipinstall--upgradepip
pipinstalljsonapi-transformerjsonapi-transformersupports Python 3.7+.Notable FeaturesCode snippets in this section use the Quick Start example above as a starting point.Support for bothidandlid.Theincludedlist is generated automatically from items inrelationshipswhento_jsonapi()is called -- there's no need to manipulate an item in both theincludedlistandan object'srelationships.Convenience method togetanattributeorrelationshipor a default if the key is not found.# First, try `transformer.attributes["greeting"]` -- greeting is not found!# Next, try `transformer.relationships["greeting"]` -- greeting is not found!# Finally, the default is returned.>>>default="hello">>>greeting=transformer.get("greeting",default)>>>print(greeting)helloConvenience accessor forgettingattributesandrelationshipsusing[]notation.# First, try `transformer.attributes["title"]` -- title is found!>>>print(transformer["title"])RailsisOmakase# First, try `transformer.attributes["author"]` -- author is not found!# Next, try `transformer.relationships["author"]` -- author is found!>>>author=transformer["author"]>>>print(author.to_jsonapi()){"data":{"type":"people","id":"9"}}Convenience accessor forsettingattributesusing[]notation.>>>transformer["my_new_attribute"]="hello"Convenience accessor fordeletingattributesusing[]notation.>>>deltransformer["my_new_attribute"]Convenience accessor forcontainsinattributesandrelationshipsusing theinkeyword.>>>"title"intransformerTrue>>>"author"intransformerTrue>>>"book"intransformerFalseDeep equality test between transformers, comparing all of:type_nameidlidattributesrelationshipsincluded>>>book=JSONAPITransformer(type_name="book",id="1")>>>article=JSONAPITransformer(type_name="article",id="1")>>>book==articleFalseDerived transformer classes can contain business logic.fromtransformersimportJSONAPITransformer,JSONAPITransformerFactoryclassPeople(JSONAPITransformer):type_name="people"@propertydeffull_name(self):"""Custom business logic for this class, keyed on `type_name`."""returnf"{self['last_name'],self['first_name']}"jsonapi={"data":{"type":"articles","id":"1","attributes":{"title":"Rails is Omakase"},"relationships":{"author":{"data":{"type":"people","id":"9"}}}},"included":[{"type":"people","id":"9","attributes":{"first_name":"Jon","last_name":"George"}}]}# By using a factory instead of `from_jsonapi_generic(...)`, we can provide a list# of classes that are instantiated based on the `type` field in the jsonapi data.# Since we didn't provide a class for the "articles" type, setting `allow_generic`# to True allows the "articles" object to load as a generic JSONAPITransformerfactory=JSONAPITransformerFactory([People],allow_generic=True)>>>transformer=factory.from_jsonapi(jsonapi)>>>print(type(transformer))<class'JSONAPITransformer'>>>>author=transformer["author"]>>>print(type(author))<class'People'># Access business logic unique to the `People` class.>>>print(author.full_name)George,JonLists of JSON:API objects can reside in the same document, and any shared relationships are de-duplicated in theincludedlist.fromtransformersimportJSONAPIListTransformer,JSONAPITransformer,from_jsonapi_genericarticle1=JSONAPITransformer(type_name="articles",id="1",attributes={"title":"Rails is Omakase",},relationships={"author":JSONAPITransformer(type_name="people",id="9",attributes={"first_name":"Jon","last_name":"George"})})article2=JSONAPITransformer(type_name="articles",id="2",attributes={"title":"Now is better than never.",},relationships={"author":JSONAPITransformer(type_name="people",id="9",attributes={"first_name":"Jon","last_name":"George"})})>>>transformer=JSONAPIListTransformer([article1,article2])>>>print(type(transformer))<class'JSONAPIListTransformer'># The `included` section is deduplicated.>>>jsonapi=transformer.to_jsonapi()>>>print(json.dumps(jsonapi,indent=4)){"data":[{"type":"articles","id":"1","attributes":{"title":"Rails is Omakase"},"relationships":{"author":{"data":{"type":"people","id":"9"}}}},{"type":"articles","id":"2","attributes":{"title":"Now is better than never."},"relationships":{"author":{"data":{"type":"people","id":"9"}}}}],"included":[{"type":"people","id":"9","attributes":{"first_name":"Jon","last_name":"George"}}]}# Convert back to transformers.>>>transformers=from_jsonapi_generic(jsonapi)>>>print(type(transformers))<class'list'>>>>forxintransformers:...print(type(x))<class'JSONAPITransformer'><class'JSONAPITransformer'>UnsupportedThesetop-level membersof the JSON:API specification are unsupported:errorsjsonapilinksmetaDevelopment LocallyContributors tojsonapi-transformercan install an editable version of this library after cloning the repository:pipinstall--upgradepip
pipinstall-e.[tests,dev]Development with DockerContributors tojsonapi-transformercan do all development tasks within a Docker container:BuildDon't forget the trailing.!!!dockerbuild-fDockerfile.testing\--build-argPYTHON_VERSION=3.10\-tjsonapi-transformer:latest\.Run Tests with Coveragedockerrun--rm-it\--entrypointpytest\jsonapi-transformer:latest\--cov--cov-report=term-missingRun Type Checkingdockerrun--rm-it\--entrypointmypy\jsonapi-transformer:latest\--show-error-context\--show-error-codes\--strict\src