Parse Array of JSON Objects in Crystal lang - arrays

Suppose I've got a simple JSON mapped object in Crystal lang, e.g.:
class Item
JSON.mapping(
id: UInt32,
name: String,
)
end
I can parse individual objects from JSON strings easily like so:
foo = Item.from_json(%({"id":1,"name":"Foo"}))
puts "OK: foo=#{foo}"
# => OK: foo=Item(#id=1, #name="Foo")
But how would I parse an array of Items from a JSON string? I've tried a few approaches but am not sure how to proceed, e.g.:
items_str = %([{"id":1,"name":"Foo"},{"id":2,"name":"Bar"}])
items : Array(Item) = JSON.parse(items_str)
# => Error in foo.cr:15: type must be Array(Item), not JSON::Any
Of course, I'd also like to be able to do this with a JSON pull parser, so presumably there's some mapping trick or type hint I'm missing. Ideas?

Found it in this spec. So, you can use Array(Item).from_json:
items = Array(Item).from_json %([{"id":1,"name":"Foo"},{"id":2,"name":"Bar"}])
items.first.id #=> 1
items.first.name #=> "Foo"
items.last.id #=> 2
items.last.name #=> "Bar"

Related

Convert Array of objects to Array with only values

I have an array (obj_values) of objects
[
#<User id: 1, name: "Kostas">,
#<User id: 2, name: "Moufa">,
...
]
And I want to convert this into an Array with only the values from above objects, so it will look like:
[
1, Kostas
2, Moufa
]
I can do it like this:
obj_table = []
obj_values.each do |ext|
ext.each do |obj|
obj_table.push([obj.id, obj.name].join(","))
end
end
However with this approach I need to explicitly specify what attributes I want to push (obj.id and obj.name), is there a way to push whole data from object without the need to specify each attribute separately?
Use .attributes to get a hash of all the attributes on the model. Call .values on it to get just the values without the keys.
Using your code:
obj_table = []
obj_values.each do |ext|
ext.each do |obj|
obj_table.push(obj.attributes.values.join(","))
end
end
Though there are better ways. I suggest you look into .map and .flat_map.
obj_table = obj_values.flat_map do |ext|
ext.map do |obj|
obj.attributes.values.join(",")
end
end
Yes, you can push data without specifying it separately. to push data you have to use an object.attributes.values
an object will be your active record object.
attributes will return a hash with all attributes of that object.
so you can change your loops into a single statement like:
obj_values.flat_map {|object_value| object_value.attributes.values }

How can I pass an object like a list or dictionary in python behave .feature file

How can I pass an object like a list or dictionary as an argument in behave .feature file so I can use that argument in my python function step? See an example of what I'm trying to achieve below:
Feature:
Scenario: Given the inputs below
Given a "<Dictionary>" and "<List>"
When we insert "<Dictionary>" and "<List>"
Then we confirm the result in the database
Examples: Input Variables
|Input1 |Input2 |
|Dictionary(json) |List |
You can provide the data as json, and parse it using json.loads in the steps.
Note, to use Examples: we need a Scenario Outline instead of a
Scenario.
# features/testing_objects.feature
Feature: Testing objects
Scenario Outline: Given the inputs below
Given a <Dictionary> and <List>
When we insert them
Then we confirm the result in the database
Examples: Input Variables
|Dictionary |List |
|{"name": "Fred", "age":2} |[1,2,"three"]|
Parse it using json.loads in the steps:
# features/steps/steps.py
import json
from behave import given, when, then
#given('a {dictionary} and {a_list}')
def given_dict_and_list(context, dictionary, a_list):
context.dictionary = json.loads(dictionary)
context.a_list = json.loads(a_list)
#when('we insert them')
def insert_data(context):
print('inserting dictionary', context.dictionary)
print('inserting list', context.a_list)
#then('we confirm the result in the database')
def confirm(context):
print('checking dictionary', context.dictionary)
print('checking list', context.a_list)
Instead of using Examples: you could also use a multi-line string literal
and then access each object in a separate step, via context.text.
Feature: String literal JSON
Scenario:
Given a dictionary
"""
{
"name": "Fred",
"age": 2
}
"""
And a list
"""
[1, 2, "three"]
"""
Then we can check the dictionary
And check the list
#given('a dictionary')
def given_a_dictionary(context):
context.dictionary = json.loads(context.text)
#given('a list')
def given_a_list(context):
context.a_list = json.loads(context.text)
#then('we can check the dictionary')
def check_the_dictionary(context):
assert context.dictionary == {
'name': 'Fred',
'age': 2
}
#then('check the list')
def check_the_list(context):
assert context.a_list == [1, 2, 'three']

Ruby Array Search Method Not Working As Intended

I am having trouble making the search method that i wrote in my script to work. Here is the relevant code from the script:
$records = []
def xml_extractor(document)
document.xpath("//record").each do |record|
$records << {"id" => record.xpath('id').text,
"first_name" => record.xpath('first_name').text,
"last_name" => record.xpath('last_name').text,
"email" => record.xpath('email').text,
"gender" => record.xpath('gender').text,
"ip_address" => record.xpath('ip_address').text,
"send_date" => record.xpath('send_date').text,
"email_body" => record.xpath('email_body').text,
"email_title" => record.xpath('email_title').text}
puts record
end
puts "\nAll records loaded!"
end
def search_by_ip(ip)
record_to_return = $records.select {|k| k["ip_address"] == ip.to_s}
puts JSON.pretty_generate(record_to_return)
end
Basically my xml_extractor method works fine and it stores everything into the array using nokogiri. The xml file that is being implemented has a thousand records each having its own first_name, last_name etc. But the problem is when i try to implement the search_by_ip method on the array a "null" value is returned, when what the method should really be doing is returning the entire record that belongs to that specific ip address. Also, i realised that every time i implement the xml_extractor method, i.e. when an xml document is parsed in into the array, the contents arent really saved in rather they are only displayed for while the loop is going. Which might be why I get a "null" for my search methods. Let me know what you guys think though.
I wrote an example of how to use OO to obtain what you want.
I don't have your document so I simplified your document to a 2 dimensional array
In the method read switch the comment to work with your xml
Each method can be chained and does only what is required
They can all be tested separatly (here by p'ting them)
class Xml_extractor
attr_reader :document, :records
def initialize document
#document = document
#records = []
end
def read
# #document.xpath("//record").each do |record|
#document.each do |record|
#records << {id: record[0], ip_address: record[1]}
end
self # return self so that you can chain another method
end
def search_by_ip(ip)
#return first of an array if found, nil otherwise
# attention to use a hash key here to search, not a string
#records.select {|k| k[:ip_address] == ip.to_s}.first
end
end
document = [[0, "192.168.0.1"], [1, "192.168.0.2"]]
p Xml_extractor.new(document).read.records
# [{:id=>0, :ip_address=>"192.168.0.1"}, {:id=>1, :ip_address=>"192.168.0.2"}]
p Xml_extractor.new(document).read.search_by_ip("192.168.0.2")
# [{:id=>1, :ip_address=>"192.168.0.2"}]
p Xml_extractor.new(document).read.search_by_ip("192.168.0.2").to_json
# "[{\"id\":1,\"ip_address\":\"192.168.0.2\"}]"
In ruby your method will return the last line. If you want your method to return data, you need to return it on the last line. puts doesn't return anything.
Try to change like this:
def search_by_ip(ip)
record_to_return = $records.select {|k| k["ip_address"] == ip.to_s}
puts JSON.pretty_generate(record_to_return)
record_to_return
end

Accessing json generated by stringify

I have an array in which data from db is stored. I have converted this array into JSON by stringify which results in json like this:
var view_data = [{ f o o = " boo " , t e x t = "t e s t " }];
When i try to access this foo object via for loop like this :
for(var d=0; d<view_data.length; d++)
{
print view_data[d].foo;
}
It gives undefined. I think this is not actually json object is array of strings.
Please help me to access the key-value pair.
You seem to be fuzzy on the meaning of JSON. JSON is a string-based format used for exchanging data. It's not used, and is irrelevant, in the course of normal programming in JavaScript, unless you're sending data to somewhere, or receiving data from somewhere, in JSON format.
I have converted this array into JSON by stringify which results in json like this:
var view_data = [{ f o o = " boo " , t e x t = "t e s t " }];
JSON.stringify takes a JavaScript object and converts it into a string-based format. Therefore this is impossible. JSON.stringify would always return a string, not a (broken) JS object as you have shown.
In your sample data, you have shown extra spaces in the property names: "f o o" instead of "foo". If in fact that's how you have attempted to specify your JS object, it won't work at all. It's invalid syntax. It is also invalid syntax to use an equal sign; you need a colon.
If you define your JS object correctly, then the code you showed will work fine:
var view_data = [{foo: "boo" , text: "test"}];
for (var d=0; d<view_data.length; d++) {
print view_data[d].foo;
}
Accessing json generated by stringify
You don't access JSON generated by stringify. JSON is a string format meant only use in communicating with some external system.
how should then access the array as json object
There is no such thing as a JSON object. There is JSON, which is a string; and there are JavaScript objects.
For more information on JavaScript objects vs. JSON, see How to parse variables to JSON Object (Javascript).

Json creation in ruby for a list

I am new to Ruby.
I want to create a JSON file for a group of elements.
For this, I am using eachfunction to retrieve the datas. I want to create json as follows for the 4 length array,
'{
"desc":{
"1":"1st Value",
"2":"2nd value"
"3":"3rd Value",
"4":"4th value"
},
}'
This is my array iteration,
REXML::XPath.each( doc, "//time" ) { |element1|
puts element1.get_text
}
I know here is the simple code to generate a JSON,
require 'json/add/core'
class Item < Struct.new(:id, :name); end
chair = Item.new(1, 'chair')
puts JSON.pretty_generate(chair)
This syntax will generate a json as follows,
{
"json_class": "Item",
"v": [
1,
"chair"
]
}
But I'm not sure how to do that to make JSON for my elements as stated above. Google search didn't give me a proper way to do this.
Can anyone help me here?
it means this?:
require 'json'
my_arr= ["1st Value","2nd Value","3rd Value","4th Value"]
tmp_str= {}
tmp_str["desc"] = {}
my_arr.each do |x|
tmp_str["desc"]["#{x[0]}"] = x
end
puts JSON.generate(tmp_str)
you can iterate the string array ,then take the strings to hash object.JSON can easy to parse Hash objcect .

Resources