Proper way to Parse a Payload in Ruby - ruby

I have the following payload:
[{:payload=>
"{\"user\":\"test\",\"job\":\"Test\",\"username\":\"Bob\",\"blocks\":[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"this is the title\"}},{\"type\":\"context\",\"elements\":[{\"type\":\"mrkdwn\",\"text\":\"Test\"}]},{\"type\":\"divider\"}]}"}]
I'm trying to figure out how to extract it. I tried
JSON.parse(response)
But I get the following error
TypeError: no implicit conversion of Hash into String
How can I extract this value to something where I can do something like:
response.job == "test" ?

Let's assume that you meant to say:
response = [{:payload => "{\"user\":\"test\",\"job\":\"Test\",\"username\":\"Bob\",\"blocks\":[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"this is the title\"}},{\"type\":\"context\",\"elements\":[{\"type\":\"mrkdwn\",\"text\":\"Test\"}]},{\"type\":\"divider\"}]}"}]
Then response is an array with one element. That one element is a hash. You would thus access the payload with:
payload = JSON.parse(response.first[:payload])
=> {
"user" => "test",
"job" => "Test",
"username" => "Bob",
"blocks" => [
[0] {
"type" => "section",
"text" => {
"type" => "mrkdwn",
"text" => "this is the title"
}
},
[1] {
"type" => "context",
"elements" => [
[0] {
"type" => "mrkdwn",
"text" => "Test"
}
]
},
[2] {
"type" => "divider"
}
]
}
The payload object is then a hash and its child elements can be accessed using the standard [] call:
job = payload['job']
=> "Test"

Related

logstash - map to json array with transformation

I have the following json file, each line is a diferent json:
{"s":"some address","c":"some city"}
{"s":"some address1","c":"some city1"}
{"s":"some address2","c":"some city2"}
I have the following job:
input {
file {
start_position => "beginning"
path => "/sources/someFile.txt"
}
}
filter {
json {
source => "a"
target => "addresses[0].street"
}
mutate {
remove_field => ["message", "#timestamp", "host", "path", "#version"]
}
}
output {
elasticsearch {
hosts => "http://elasticsearch:9200"
index => "store"
}
}
I want to write to to the index as the following (each address go to a different doc as the forst element in an array):
{
"addresses": [{"street" : "some address", "city" : "some city"}]
}
{
"addresses": [{"street" : "some address2", "city" : "some city1"}]
}
{
"addresses": [{"street" : "some address3", "city" : "some city2"}]
}
The attached job is not working. no error and not doing anything.
Thanks
You cannot use that field reference in the target option of the json filter. In any version of logstash from the last couple of years I would expect that to result in a _jsonparsefailure tag and the error
Exception caught in json filter {:exception=>"Invalid FieldReference: `addresses[0].street`"
If you change the reference to be [addresses][0] then it will run without error, but the reference will be interpreted as the "0" entry in the "addresses" hash, not the first entry in the addresses array.
Your incoming JSON has the wrong field names, so you will have to rename the fields. I think it is easiest to do it in a ruby filter
json { source => "message" target => "[#metadata][json]" }
ruby {
code => '
json = event.get("[#metadata][json]")
event.set("addresses", [ { "street" => json["s"], "city" => json["c"] } ] )
'
}
which produces
"addresses" => [
[0] {
"city" => "some city",
"street" => "some address"
}
],
The original JSON is placed inside the [#metadata] field so that is available but not indexed by the output.

Logstash - how do I split an array using the split filter without a target?

I'm trying to split a JSON array into multiple events. Here's a sample input:
{"results" : [{"id": "a1", "name": "hello"}, {"id": "a2", "name": "logstash"}]}
Here's my filter and output config:
filter {
split {
field => "results"
}
}
stdout {
codec => "rubydebug"
}
This produces 2 events, one for each of the JSONs in the array. And it's close to what I'm looking for:
{
"results" => {
"id" => "a1",
"name" => "hello"
},
"#version" => "1",
"#timestamp" => "2015-05-30T18:33:21.527Z",
"host" => "laptop",
}
{
"results" => {
"id" => "a2",
"name" => "logstash"
},
"#version" => "1",
"#timestamp" => "2015-05-30T18:33:21.527Z",
"host" => "laptop",
}
The problem is the nested "results" part. "results" being the default value for the target parameter.
Is there a way to use the split filter without producing the nested JSON, and get something like this:
{
"id" => "a1",
"name" => "hello"
"#version" => "1",
"#timestamp" => "2015-05-30T18:33:21.527Z",
"host" => "laptop",
}
{
"id" => "a2",
"name" => "logstash"
"#version" => "1",
"#timestamp" => "2015-05-30T18:33:21.527Z",
"host" => "laptop",
}
The purpose is to feed this to the ElasticSearch output with each event being a document with document_id => "id". Any good solutions are welcomed!
If you know what all of the fields will be (as it appears you do), you can simply rename the fields:
mutate {
rename => [
"[results][id]", "id",
"[results][name]", "name"
]
remove_field => "results"
}
If you didn't know what all of the fields were, you could write a ruby code filter that did a event['results'].each... and created new fields from the sub-fields of results.

How do I convert an Array with a JSON string into a JSON object (ruby)

I have an Array whose content is as follow:
[
[0] {
"name" => “Mark”,
"id" => “01”,
"description" => “User”,
},
[1] {
"name" => “John”,
"id" => “02”,
"description" => “Developer”,
}
]
Note: right now each item of the Array is a Hash (not a string). That is to say that if I do puts myarray[0].class I get hash in return.
I would like to be able to create an object that I can reference as object[i].field.
For example I'd like to be able to get "Mark" by calling object[0].name or get "Developer" by calling object[1].description.
Is this possible? I have tried to leverage the .to_json method against my array but it doesn't quite give me what I need.
Thanks.
You can do use Struct to meet your need.
array = [
{
"name" => "Mark",
"id" => "01",
"description" => "User",
},
{
"name" => "John",
"id" => "02",
"description" => "Developer",
}
]
Customer = Struct.new(:name, :id, :description)
array_of_customers = array.map { |hash| Customer.new(*hash.values) }
array_of_customers[1].name # => "John"
array_of_customers[1].description # => "Developer"

How do I access JSON array data?

I have the following array:
[ { "attributes": {
"id": "usdeur",
"code": 4
},
"name": "USD/EUR"
},
{ "attributes": {
"id": "eurgbp",
"code": 5
},
"name": "EUR/GBP"
}
]
How can I get both ids for futher processing as output?
I tried a lot but no success. My problem is I always get only one id as output:
Market.all.select.each do |market|
present market.id
end
Or:
Market.all.each{|attributes| present attributes[:id]}
which gives me only "eurgbp" as a result while I need both ids.
JSON#parse should help you with this
require 'json'
json = '[ { "attributes": {
"id": "usdeur",
"code": 4
},
"name": "USD/EUR"
},
{ "attributes": {
"id": "eurgbp",
"code": 5
},
"name": "EUR/GBP"
}]'
ids = JSON.parse(json).map{|hash| hash['attributes']['id'] }
#=> ["usdeur", "eurgbp"]
JSON#parse turns a jSON response into a Hash then just use standard Hash methods for access.
I'm going to assume that the data is JSON that you're parsing (with JSON.parse) into a Ruby Array of Hashes, which would look like this:
hashes = [ { "attributes" => { "id" => "usdeur", "code" => 4 },
"name" => "USD/EUR"
},
{ "attributes" => { "id" => "eurgbp", "code" => 5 },
"name" => "EUR/GBP"
} ]
If you wanted to get just the first "id" value, you'd do this:
first_hash = hashes[0]
first_hash_attributes = first_hash["attributes"]
p first_hash_attributes["id"]
# => "usdeur"
Or just:
p hashes[0]["attributes"]["id"]
# => "usdeur"
To get them all, you'll do this:
all_attributes = hashes.map {|hash| hash["attributes"] }
# => [ { "id" => "usdeur", "code" => 4 },
# { "id" => "eurgbp", "code" => 5 } ]
all_ids = all_attributes.map {|attrs| attrs["id"] }
# => [ "usdeur", "eurgbp" ]
Or just:
p hashes.map {|hash| hash["attributes"]["id"] }
# => [ "usdeur", "eurgbp" ]
JSON library what using Rails is very slowly...
I prefer to use:
gem 'oj'
from https://github.com/ohler55/oj
fast and simple! LET'S GO!

Ruby: including a hash inside another hash

I have the following hash:
EMAIL_PWD_HASH = Hash.new
EMAIL_PWD_HASH[ "email" ] = { "label" => EMAIL_STR, "type" => "email_field" }
EMAIL_PWD_HASH[ "password" ] = { "label" => PWD_STR, "type" => "password_field" }
and the following hash:
NEW_USER_HASH = Hash.new
NEW_USER_HASH[ "first" ] = { "label" => FIRST_STR, "type" => "text_field" }
NEW_USER_HASH[ "last" ] = { "label" => LAST_STR, "type" => "text_field" }
NEW_USER_HASH[ "title" ] = { "label" => TITLE_STR, "type" => "text_field" }
NEW_USER_HASH[ "bio" ] = { "label" => BIO_STR, "type" => "text_field" }
I would like to add email and password to NEW_USER_HASH after last and before bio. What is the syntax for adding EMAIL_PWD_HASH (the order is important)?
NEW_USER_HASH.merge!(EMAIL_PAD_HASH)
Note also that hashes in ruby are not ordered.
I don't know how to do what you asked, and I doubt it's possible, but here's a quick and dirty way to do what you need:
NEW_USER_HASH['email'] = EMAIL_PWD_HASH['email']
NEW_USER_HASH['password'] = EMAIL_PWD_HASH['password']
NEW_USER_HASH['bio'] = NEW_USER_HASH.delete('bio') # deletes bio and reinsert in the end
email and password are now after last and before bio, as you asked. :)

Resources