Ruby gem for deserializing JSON API payloads into custom hashes.
- Chat: gitter
- Twitter: @jsonapirb
- Docs: jsonapi-rb.org
See jsonapi-rb.org/guides/deserialization.
jsonapi-deserializable is released under the MIT License.
Conveniently deserialize JSON API payloads into custom hashes.
Home Page: http://jsonapi-rb.org
License: MIT License
Ruby gem for deserializing JSON API payloads into custom hashes.
See jsonapi-rb.org/guides/deserialization.
jsonapi-deserializable is released under the MIT License.
Hi
I'm working in a little Sinatra app and found that after deserializing with the Resource
class defined I here, I can't seem to get to the attributes because the to_h
method give me nothing.
The get_jsonapi
method was defined elsewhere and does the http call and returns the response data that lives under "data" key.
As you can see, the deserializer properly pulls the attribute out but does not fill the @hash
attribute so the to_h
doesn't give me any data back.
# in a console
> jsonapi = get_jsonapi("studios/1890-bryant-street-studios.json")
=> {"id"=>"3",
"type"=>"studio",
"attributes"=>
{"name"=>"1890 Bryant Street Studios",
"street_address"=>"1890 bryant st",
"city"=>"New Ginaberg",
"map_url"=>"http://maps.google.com/maps?q=1890+bryant+st%2C+New+Ginaberg%2C+Kansas%2C+87776",
"url"=>"/studios/1890-bryant-street-studios",
"slug"=>"1890-bryant-street-studios"},
"relationships"=>{"artists"=>{"meta"=>{"count"=>27}}}}
> studio = JSONAPI::Deserializable::Resource.new(jsonapi)
=> #<JSONAPI::Deserializable::Resource:0x00007fc8663fcab0
@attributes=
{"name"=>"1890 Bryant Street Studios",
"street_address"=>"1890 bryant st",
"city"=>"New Ginaberg",
"map_url"=>"http://maps.google.com/maps?q=1890+bryant+st%2C+New+Ginaberg%2C+Kansas%2C+87776",
"url"=>"/studios/1890-bryant-street-studios",
"slug"=>"1890-bryant-street-studios"},
@data=
{"id"=>"3",
"type"=>"studio",
"attributes"=>
{"name"=>"1890 Bryant Street Studios",
"street_address"=>"1890 bryant st",
"city"=>"New Ginaberg",
"map_url"=>"http://maps.google.com/maps?q=1890+bryant+st%2C+New+Ginaberg%2C+Kansas%2C+87776",
"url"=>"/studios/1890-bryant-street-studios",
"slug"=>"1890-bryant-street-studios"},
"relationships"=>{"artists"=>{"meta"=>{"count"=>27}}}},
@hash={},
@id="3",
@relationships={"artists"=>{"meta"=>{"count"=>27}}},
@reverse_mapping={},
@root="/data",
@type="studio">
> studio.to_h
=> {}
Am I mis-using things? Or is this a bug?
I did a tiny bit of digging and it seems that in resource.rb
where you define
def deserialize_attr(key, val)
block = self.class.attr_blocks[key] || self.class.default_attr_block
return {} unless block
the block
is actually nil
. Is that expected? Am I supposed to define a default_attr_block
?
This may or may not be needed/wanted.
Hi
I am trying to use DeserializableResourceType.call() but receiving a {} return value.
I am not sure what the format for the blocks (type, attributes, relationship and id ) in the JSONAPI::Deserializable::Resource would be.
Thank you very much
I'm trying to create a new resource with an existing template
relationship and an address
relationship that should become a new resource. Example POST request:
{
"data": {
"type": "post",
"attributes": {
"title": "foo"
},
"relationships": {
"template": {
"data": {
"type": "templates",
"id": "1"
}
},
"address": {
"data": {
"type": "addresses",
"first_name": "John",
"last_name": "Doe"
}
}
}
}
}
Because the address
relationship does not contain an ID, it throws the following error:
A resource identifier object MUST contain ['id', 'type'] members.
I get that, because the JSON:API documentation clearly states:
A resource object MUST contain at least the following top-level members:
- id
- type
However, the documentation contains an important exception:
Exception: The id member is not required when the resource object originates at the client and represents a new resource to be created on the server.
That's exactly what I'm doing. Can we create an exception for this, or is this is incorrect way?
There is an issue in case payload deserialization like this happens:
{
"data":{
"type":"billing_period",
"attributes":{
"invoiceType":"prepayment",
"billingPeriodStart":"2022-06-01",
"billingPeriodEnd":"2022-07-01",
"amount":"2000"
},
"relationships":{
"invoice":{
"data":{
"type":"invoice",
"id":"42b2a67b-5c50-4d8f-b91e-e4269933b007"
}
}
}
}
}
In this case due to hash merging order relationships always take precedence over attributes,
so the deserialized attribute invoiceType
in the case above will be invoice
instead of expected prepayment
.
The fix may be as easy as switching the order for deserialize_rels
and deserialize_attrs
hashes = [deserialize_type, deserialize_id,
deserialize_rels, deserialize_attrs]
though it's not backwards compatible.
Please suggest,
Leo
I've been checking back periodically to see if the docs have been written for deserialization, but sadly they haven't.
Any chance even a basic usage of this could be sketched out?
Thanks for your work.
This can currently be achieved by setting a custom default attribute deserialization proc.
JSONAPI::Deserializable.configure do |config|
config.default_attribute = proc do |key, value|
{ key.capitalize.to_sym => value }
end
end
Hi,
My app refuses to start when I set Rails.env
to production
event though it successfully starts when set to development
:
Backtrace when I launch the console for example, bundle exec rails console production
:
/my_home/my_app/vendor/bundle/gems/jsonapi-deserializable-0.2.0/lib/jsonapi/deserializable/resource/dsl.rb:15:in `type': wrong number of arguments (given 1, expected 0) (ArgumentError)
from /my_home/my_app/app/deserializers/deserializable_user.rb:2:in `<class:DeserializableUser>'
from /my_home/my_app/app/deserializers/deserializable_user.rb:1:in `<top (required)>'
from /my_home/my_app/vendor/bundle/gems/activesupport-5.1.4/lib/active_support/dependencies.rb:476:in `load'
from /my_home/my_app/vendor/bundle/gems/activesupport-5.1.4/lib/active_support/dependencies.rb:476:in `block in load_file'
from /my_home/my_app/vendor/bundle/gems/activesupport-5.1.4/lib/active_support/dependencies.rb:661:in `new_constants_in'
from /my_home/my_app/vendor/bundle/gems/activesupport-5.1.4/lib/active_support/dependencies.rb:475:in `load_file'
from /my_home/my_app/vendor/bundle/gems/activesupport-5.1.4/lib/active_support/dependencies.rb:374:in `block in require_or_load'
from /my_home/my_app/vendor/bundle/gems/activesupport-5.1.4/lib/active_support/dependencies.rb:36:in `block in load_interlock'
from /my_home/my_app/vendor/bundle/gems/activesupport-5.1.4/lib/active_support/dependencies/interlock.rb:12:in `block in loading'
from /my_home/my_app/vendor/bundle/gems/activesupport-5.1.4/lib/active_support/concurrency/share_lock.rb:149:in `exclusive'
from /my_home/my_app/vendor/bundle/gems/activesupport-5.1.4/lib/active_support/dependencies/interlock.rb:11:in `loading'
from /my_home/my_app/vendor/bundle/gems/activesupport-5.1.4/lib/active_support/dependencies.rb:36:in `load_interlock'
from /my_home/my_app/vendor/bundle/gems/activesupport-5.1.4/lib/active_support/dependencies.rb:357:in `require_or_load'
from /my_home/my_app/vendor/bundle/gems/activesupport-5.1.4/lib/active_support/dependencies.rb:335:in `depend_on'
from /my_home/my_app/vendor/bundle/gems/activesupport-5.1.4/lib/active_support/dependencies.rb:251:in `require_dependency'
from /my_home/my_app/vendor/bundle/gems/railties-5.1.4/lib/rails/engine.rb:476:in `block (2 levels) in eager_load!'
from /my_home/my_app/vendor/bundle/gems/railties-5.1.4/lib/rails/engine.rb:475:in `each'
from /my_home/my_app/vendor/bundle/gems/railties-5.1.4/lib/rails/engine.rb:475:in `block in eager_load!'
from /my_home/my_app/vendor/bundle/gems/railties-5.1.4/lib/rails/engine.rb:473:in `each'
from /my_home/my_app/vendor/bundle/gems/railties-5.1.4/lib/rails/engine.rb:473:in `eager_load!'
from /my_home/my_app/vendor/bundle/gems/railties-5.1.4/lib/rails/engine.rb:354:in `eager_load!'
from /my_home/my_app/vendor/bundle/gems/railties-5.1.4/lib/rails/application/finisher.rb:67:in `each'
from /my_home/my_app/vendor/bundle/gems/railties-5.1.4/lib/rails/application/finisher.rb:67:in `block in <module:Finisher>'
from /my_home/my_app/vendor/bundle/gems/railties-5.1.4/lib/rails/initializable.rb:30:in `instance_exec'
from /my_home/my_app/vendor/bundle/gems/railties-5.1.4/lib/rails/initializable.rb:30:in `run'
from /my_home/my_app/vendor/bundle/gems/railties-5.1.4/lib/rails/initializable.rb:59:in `block in run_initializers'
from /my_home/.rbenv/versions/2.4.3/lib/ruby/2.4.0/tsort.rb:228:in `block in tsort_each'
from /my_home/.rbenv/versions/2.4.3/lib/ruby/2.4.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
from /my_home/.rbenv/versions/2.4.3/lib/ruby/2.4.0/tsort.rb:431:in `each_strongly_connected_component_from'
from /my_home/.rbenv/versions/2.4.3/lib/ruby/2.4.0/tsort.rb:349:in `block in each_strongly_connected_component'
from /my_home/.rbenv/versions/2.4.3/lib/ruby/2.4.0/tsort.rb:347:in `each'
from /my_home/.rbenv/versions/2.4.3/lib/ruby/2.4.0/tsort.rb:347:in `call'
from /my_home/.rbenv/versions/2.4.3/lib/ruby/2.4.0/tsort.rb:347:in `each_strongly_connected_component'
from /my_home/.rbenv/versions/2.4.3/lib/ruby/2.4.0/tsort.rb:226:in `tsort_each'
from /my_home/.rbenv/versions/2.4.3/lib/ruby/2.4.0/tsort.rb:205:in `tsort_each'
from /my_home/my_app/vendor/bundle/gems/railties-5.1.4/lib/rails/initializable.rb:58:in `run_initializers'
from /my_home/my_app/vendor/bundle/gems/railties-5.1.4/lib/rails/application.rb:353:in `initialize!'
from /my_home/my_app/config/environment.rb:5:in `<top (required)>'
from /my_home/my_app/vendor/bundle/gems/spring-2.0.2/lib/spring/application.rb:102:in `require'
from /my_home/my_app/vendor/bundle/gems/spring-2.0.2/lib/spring/application.rb:102:in `preload'
from /my_home/my_app/vendor/bundle/gems/spring-2.0.2/lib/spring/application.rb:153:in `serve'
from /my_home/my_app/vendor/bundle/gems/spring-2.0.2/lib/spring/application.rb:141:in `block in run'
from /my_home/my_app/vendor/bundle/gems/spring-2.0.2/lib/spring/application.rb:135:in `loop'
from /my_home/my_app/vendor/bundle/gems/spring-2.0.2/lib/spring/application.rb:135:in `run'
from /my_home/my_app/vendor/bundle/gems/spring-2.0.2/lib/spring/application/boot.rb:19:in `<top (required)>'
from /my_home/.rbenv/versions/2.4.3/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb:55:in `require'
from /my_home/.rbenv/versions/2.4.3/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb:55:in `require'
from -e:1:in `<main>'
Culprit:
class DeserializableUser < JSONAPI::Deserializable::Resource
type 'users'
attributes :first_name, :last_name, :identifier, :email
end
Any idea why it happens and how I can fix this ?
This can be useful for setting the source pointer of an error in an application.
Hi
I tried deserializing a compound json api document with the gem and found out this isn't supported as of now. For example, if the payload looks like this
{
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"links": {
"self": "http://example.com/articles/1"
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/1/relationships/author",
"related": "http://example.com/articles/1/author"
},
"data": { "type": "people", "id": "9" }
},
"comments": {
"links": {
"self": "http://example.com/articles/1/relationships/comments",
"related": "http://example.com/articles/1/comments"
},
"data": [
{ "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
]
}
}
}],
"included": [{
"type": "people",
"id": "9",
"attributes": {
"first-name": "Dan",
"last-name": "Gebhardt",
"twitter": "dgeb"
},
"links": {
"self": "http://example.com/people/9"
}
}, {
"type": "comments",
"id": "5",
"attributes": {
"body": "First!"
},
"relationships": {
"author": {
"data": { "type": "people", "id": "2" }
}
},
"links": {
"self": "http://example.com/comments/5"
}
}, {
"type": "comments",
"id": "12",
"attributes": {
"body": "I like XML better"
},
"relationships": {
"author": {
"data": { "type": "people", "id": "9" }
}
},
"links": {
"self": "http://example.com/comments/12"
}
}]
}
One would have to manually to iterate over the data
key and feed each resource into the Article deserializer.
class DeserializableArticle < JSONAPI::Deserializable::Resource
has_one :author
has_many :comments do | link, ids, types | do
....
end
end
doc = JSON.parse(document)
doc["data"].map do |resource|
DeserializableArticle.call(resource)
end
But going this way would mean other top level document related resources(the included
key) wouldn't be available in the has_many
block defined in the deserializer.
Is there a way to get the related resources added in the included
top-level part of the document into the has_many/has_one
block so we can easily get all the needed data deserialized together?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.