I'm attempting to take a JSON API response, with nested associated resources, and reverse the associations in a Rails app.
So, imagine I get a response like this:
{
"spenders": [
{
"id": 1,
"name": "John Doe",
"accounts": [
{
"id": 1,
"name": "Account One"
},
{
"id": 2,
"name": "Account Two"
}
]
},
{
"id": 2,
"name": "Jane Doe",
"accounts": [
{
"id": 1,
"name": "Account One"
},
{
"id": 3,
"name": "Account Three"
}
]
}
]
}
My goal is to convert this into structure like this:
{
"accounts": [
{
"id": 1,
"name": "Account One",
"spenders": [
{
"id": 1,
"name": "Stephen Margheim"
},
{
"id": 2,
"name": "Greg Barendt"
}
]
},
{
"id": 2,
"name": "Account Two",
"spenders": [
{
"id": 1,
"name": "Stephen Margheim"
}
]
},
{
"id": 3,
"name": "Account Three",
"spenders": [
{
"id": 2,
"name": "Greg Barendt"
}
]
}
]
}
Now, I can do this fairly well with iteration over the hash and building a new hash:
spenders_hash = {}
accounts.each do |account|
account.spenders.each do |spender|
if spenders_hash.key? spender.id
spenders_hash[spender.id][:accounts] << account
else
spenders_hash[spender.id] = hash_from_spender_and_account(spender, account)
end
end
end
spenders_hash
def hash_from_spender_and_account(spender, account)
{
id: spender.id,
name: spender.name,
accounts: [account],
}
end
I'm hoping to find [1] a more flexible solution that isn't reliant on knowing the key names in advance and [2] hopefully more efficient.
Thoughts?
Related
I'm very new to OpenAPI and I'm using http://editor.swagger.io to design an API.
I'm stuck in Schema with a JSON looking like following
{
"CORRELATION_ID": "10",
"CONTROL":
{
"DAS_IS_RECIPIENT": "123",
"DOCTPYE": "ert",
"PROCESS_INDICATOR": "nord"
},
"HEADER":
{
"ID": "456",
"INVOICE_NUMBER": "678",
"DMS_DOC_ID": "876",
"INVOICE_DATE": "10082020"
},
"ITEMS": [
{
"SHORT_TEXT": "123",
"LSTAR": 0,
"QUANTITY": "23"
},
{
"SHORT_TEXT": "456",
"LSTAR": 234,
"QUANTITY": "21"
}
],
"DEBITOR":
{
"ID": "444",
"FIRSTNAME": "nick",
"LASTNAME": "cantre"
},
"CREDITOR":
{
"ID": "454",
"FIRSTNAME": "ava",
"LASTNAME": "pierre"
}
}
How to create a schema according to this JSON structure?
The default result of rendering FastJsonApi gem serialized_json like below:
render json: FlashcardSerializer.new(flashcards).serialized_json
would be something like this:
{
"data": [
{
"id": "1",
"type": "flashcard",
"attributes": {
"question": "why?",
"answer": "pretty good",
"slug": null
}
},
{
"id": "2",
"type": "flashcard",
"attributes": {
"question": "What is 0",
"answer": "it is 0",
"slug": null
}
}
]
}
I would rather add some extra information especially for pagination and I like the result to be something like this:
{
"data": [
{
"id": "1",
"type": "flashcard",
"attributes": {
"question": "why?",
"answer": "pretty good",
"slug": null
}
},
{
"id": "2",
"type": "flashcard",
"attributes": {
"question": "What is 0",
"answer": "it is 0",
"slug": null
}
},
"count":100,
"page":1,
]
}
I am aware of other available gems that manage pagination in API, and I know how to do it without Fastjson. The main issue here is that is there any way to get the aforementioned result from this gem without changing a lot in the code. Thanks
The desired document would be invalid according to the JSON API specification. You would need to include next and previous links in a link section. The current and total_count would belong in the meta section.
{
"data": [
{
"id": "1",
"type": "flashcard",
"attributes": {
"question": "why?",
"answer": "pretty good",
"slug": null
}
},
{
"id": "2",
"type": "flashcard",
"attributes": {
"question": "What is 0",
"answer": "it is 0",
"slug": null
}
},
]
"meta": {
"page": { "current": 1, "total": 100 }
},
"links": {
"prev": "/example-data?page[before]=yyy&page[size]=1",
"next": "/example-data?page[after]=yyy&page[size]=1"
},
}
Have a look at the JSON API specification before you continue designing the API.
You can pass these information into the serializer as an options argument
class FlashcardsController < ApplicationController
def index
render json: FlashcardSerializer.new(
flashcards, { links: {}, meta: { page: { current: 1 } }
).serialized_json
end
end
How you generate the data depends what you use to paginate.
If you design a new API, I would also recommend to use cursor based pagination rather than offset pagination because of it's limitations.
https://github.com/Netflix/fast_jsonapi#compound-document
https://github.com/Netflix/fast_jsonapi/blob/master/spec/lib/object_serializer_spec.rb#L8-L32
I am creating a rails app with Backbone/Marionette frontend.
I want to take the JSON-object below and transform it.
This is how it looks now
[{
"total_entries": 4
},
{
"entries": [{
"user": {
"id": 1,
"fullname": "Paul Paulsen"
},
"reports": [{
"id": 1,
"name": "Anna Pearson",
"relation": "wife",
"phone": "2232",
"email": "adsas#sss.se"
}
]
},
{
"user": {
"id": 2,
"fullname": "Anna Palmgren"
},
"reports": [{
"id": 3,
"name": "Mika",
"relation": "Andersen",
"phone": "12312321",
"email": "aas#sss.se"
}]
}
]
}
]
I want to make it look like this
[{
"total_entries": 4
},
{
"entries": [{
"id": 1,
"fullname": "Paul Paulsen"
}, {
"id": 1,
"name": "Anna Pearson",
"relation": "wife",
"phone": "2232",
"email": "adsas#sss.se"
}, {
"id": 1,
"fullname": "Anna Palmgren"
}, {
"id": 3,
"name": "Mika",
"relation": "Andersen",
"phone": "12312321",
"email": "aas#sss.se"
}
]
}
]
This is the Rabl file/code I use
json.array! [0,1] do |index|
if index == 0
json.total_entries #total
else
json.entries #reports.group_by(&:user) do |user, reports|
json.user user, :id, :fullname
json.reports reports do |report|
json.extract! report, :id, :name, :relation, :phone, :email
end
end
end
end
Try this ruby snippet.
x= [{
"total_entries": 4
},
{
"entries": [{
"user": {
"id": 1,
"fullname": "Paul Paulsen"
},
"reports": [{
"id": 1,
"name": "Anna Pearson",
"relation": "wife",
"phone": "2232",
"email": "adsas#sss.se"
}
]
},
{
"user": {
"id": 2,
"fullname": "Anna Palmgren"
},
"reports": [{
"id": 3,
"name": "Mika",
"relation": "Andersen",
"phone": "12312321",
"email": "aas#sss.se"
}]
}
]
}
]
x[1][:entries] = x[1][:entries].map{|p| [p[:user], p[:reports]]}.flatten
I have 2 arrays and I want to combine them in one which should return nested JSON.
The first one is cleaner which returns:
{
"response": [
{
"id": 1,
"first_name": "Fernando",
"last_name": "Gomez",
"avg_rating": "4.5"
}
]
}
The second one is reviews from the clients
which returns the name of the client, comment and rating:
{
"response": [
{
"id": 1,
"score": 4,
"comment": "Comment",
"first_name": "John Doe"
}
]
}
So, I trie to zip them and loop over each of them. Here is my code:
cleaners.zip(reviews).each do |cleaner, review|
if cleaner.id == review.id
test['first_name'] = cleaner.first_name
test['last_name'] = cleaner.last_name
test['rating'] = review.score
test['comment'] = review.comment
test['client_name'] = review.first_name
end
end
The result is:
{
"response": {
"first_name": "Fernando",
"last_name": "Gomez",
"rating": 4,
"comment": "Comment",
"client_name": "John Doe"
}
}
But my result has to be nested because some of the cleaners will have many reviews. It has to be something like this:
{
"response": {
"first_name": "Fernando",
"last_name": "Gomez",
"score_from_client": [
{
"rating": 4,
"comment": "Comment",
"client_name": "John Doe"
}
]
}
}
Here is the answer of the question :)
unless cleaners.nil?
cleaner_ids = []
cleaners_reviews = []
cleaners.each do |cleaner|
cleaner_review = {
id: cleaner.id,
avg_rating: cleaner.avg_rating,
first_name: cleaner.first_name,
last_name: cleaner.last_name,
ratings: []
}
cleaners_reviews << cleaner_review
cleaner_ids << cleaner.id
end
reviews = Cleaner.get_cleaner_reviews(dates, cleaner_ids)
unless reviews.empty?
i = 0
reviews.each do |review|
cleaners_reviews[i][:ratings] << review
i += i
end
end
return cleaners_review
s
I am grabbing value data: name, uid, highschool_name, graduateschool_name like this:
def add_friends
facebook.get_connections("me", "friends", :fields => "name, id, education").each do |hash|
self.friends.where(:name => hash['name'],
:uid => hash['id'],
:highschool_name => hash['education']['school']['name'] unless hash["education"].blank?,
:graduateschool_name => hash['education']['school']['name'] unless hash["education"].blank?).
first_or_create
end
end
From an array of hash:
"education": [
{
"school": {
"id": "110703012290674",
"name": "Kunskapsgymnasiet Malmö"
},
"year": {
"id": "136328419721520",
"name": "2009"
},
"type": "High School"
},
{
"school": {
"id": "112812485399398",
"name": "Malmö University"
},
"year": {
"id": "118118634930920",
"name": "2012"
},
"concentration": [
{
"id": "104076956295773",
"name": "Computer Science"
}
],
"type": "Graduate School",
"classes": [
{
"id": "165093923542525",
"name": "Programmering",
"description": "Kursen fokuserar på metoder och tekniker vid utveckling av webbapplikationer med hjälp av HTML5."
}
]
}
],
EDIT:
This code dosent work. I would like to pick every hichschool and Graduate School from this array of hash and save it.
high_schools = response['education'].collect{|ed| ed['school']['name'] if ed['type'] == "High School" }
grad_schools = response['education'].collect{|ed| ed['school']['name'] if ed['type'] == "Graduate School" }