I have the following table:
local obj = {
firstname = "John",
lastname = "Bush",
age = 22,
height = 186,
friends = {
{ firstname = "Paul", lastname = "Graham", age = 20, height = 178 },
{ firstname = "Gianou", lastname = "Kwnstantina", age = 25, height = 172 }
},
bodies = { 4, 3, 10 }
}
and I want to transform it to this: (when inserting to database)
local tuple = {
'John', 'Bush', 22, 186, { { 'Paul', 'Graham', 20, 178 }, { 'Gianou', 'Kwnstantina', 25, 172 } }, { 4, 3, 10 }
}
which I managed to generate using:
function encode(schema, data)
local array = {}
function rec(arr, schema, d)
for i, field in ipairs(schema) do
arr[i] = {}
if field.type == "array" and type(field.items) == "table" then
for it, piece in ipairs(d[field.name]) do
arr[i][it] = {}
rec(arr[i][it], field.items, piece)
end
else
arr[i] = d[field.name]
end
end
end
rec(array, schema, data)
return array
end
My problem is that I want to reconstruct that data to the initial form when I retrieve it. I found some tools that can do this in other languages but the problem is that they transform the data to binary, I just want that array.
To be able to reconstruct I need something like a schema:
local schema = {
{ name = "firstname", type = "string" },
{ name = "lastname", type = "string" },
{ name = "age", type = "int" },
{ name = "height", type = "int" },
{ name = "friends", type = "array", items = {
{ name = "firstname", type = "string" },
{ name = "lastname", type = "string" },
{ name = "age", type = "int" },
{ name = "height", type = "int" }
}},
{ name = "bodies", type = "array", items = "int"}
}
I found that it's pretty easy to encode it just by retrieving the values but being able to validate and decode it seems pretty hard, is there a concept (name) for this operation in computer science, or a library that I can study (it needs to use a schema)?
I need a keyword to google...
Thank you!
EDIT: Just reversing the encode I found a simple way to decode.
function decode(schema, data)
local obj = {}
function rec(object, sch, d)
for i, field in ipairs(sch) do
if field.type == "array" and type(field.items) == "table" then
object[field.name] = {}
for it, piece in ipairs(d[i]) do
object[field.name][it] = {}
rec(object[field.name][it], field.items, piece)
end
else
object[field.name] = d[i]
end
end
end
rec(obj, schema, data)
return obj
end
I would still love suggestions on the code and where can I found more info on this type of info.
present version: https://pastebin.com/Ub4vZ0GU
Related
I have following data.json file:
{
"ids": {
"id": "a2mx8m6yvksgu3605c7c1a61d"
},
"second": {
"name": "test2"
},
"third": {
"name": "test3"
}
}
I did fetch a variable for the id from the json.
* def id = data.ids.id
I want to use this variable id (defined above) to a request.
Request defined below is dynamically being sent to the xml file i.e xml request is being generated with but the below doesnt work when i try to pass the variable id.
This however works when i hardcode the id value.
* def ARG = {attr: [ { regex: '#(ids)', value: '<id>"#(id)"</id>'} ] }
Please help me how can i pass the data being read from json to the above line of code.
Read the docs: https://github.com/intuit/karate#rules-for-embedded-expressions
* def id = 'foo'
* def val = '<id>' + id + '</id>'
* def arg = { value: '#(val)' }
* match arg == { value: '<id>foo</id>' }
I have this Javascript object:
{ person: { name: "john", age: 32 }}
And an endpoint returning:
{
"name": "john",
"age": 32
}
I have this steps:
Given path 'endpoint/'
When method get
Then status 200
And match response ==
"""
{
"name": #(person.name),
"age": #(person.age)
}
"""
This is not working because #(person.name) and #(person.age) both evaluate to null, how can I fix it? (putting them in a new variable is not the fix I'm looking for)
Here you go:
* def data = { person: { name: 'john', age: 32 } }
* def response = { name: 'john', age: 32 }
* match response == data.person
Since you appear to be confused, let me add this (but not recommended because of the above):
* match response == { name: '#(data.person.name)', age: '#(data.person.age)' }
I want to update a value of somewhereInJsonPath field in my JSON file.
I am using for this: * set myBody $..someWhereInJsonPath = 'AAA'. And when I run test I get: Path must not end with a '.'
But when I am using * set myBody $..firstHere.someWhereInJsonPath = 'AAA' it is working.
I think in first case, where we want to update first value in $.., it must working too.
To clarify:
For example we have JSON:
{
"store": {
"book": [
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"something": 12.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
},
"expensive": 10
}
And when I do: $.store.book[0].something = 13 it is working.
BUT when I do: $..something = 13 it is not working.
Why? And how can I update this?
At http://jsonpath.com/ when I want to find $..something it is find this value. But when I use $..something in karate it is not working.
Realted with https://stackoverflow.com/questions/54928387/karate-jsonpath-wildcards-didnt-work-or-partly-didnt-work
Actually the set command is not really designed for JsonPath wildcards. For example $[*].foo or $..foo are examples of wildcards. And $[0].foo, $.foo or response.foo are pure JS expressions.
So please stick to pure JS expressions like this. Here below, set is interchangeable with eval which is more useful in case you want to use dynamic / variables for e.g. * eval response[foo] where foo is a string.
* def response = { foo: { somePath: 'abc' } }
* eval response.foo.somePath = 'xyz'
* match response == { foo: { somePath: 'xyz' } }
If you really do need to do "bulk" updates, use a transform function:
* def response = [{}, {}]
* def fun = function(x, i){ return { foo: 'bar', index: ~~(i + 1) } }
* def res = karate.map(response, fun)
* match res == [{ foo: 'bar', index: 1 }, { foo: 'bar', index: 2 }]
I want to use JsonConvert.Serialize in order to serialize a c# array class object into a json non-array object.
public list<employee> employees;
Output:
"{\"employees\":
{\"name\":\"Alex\",\"number\":\"25860340\"},
{\"name\":\"Tom\",\"number\":\"94085345\"}
}"
The format you have asked for in your question would not be valid JSON, because objects are not allowed to follow one another directly unless they are part of an array (see JSON.org). However, you could transform your employee list into a dictionary and serialize that instead, so long as you had a suitable key to use. One idea would be to use the employee number as a key, for example:
var employees = new List<Employee>
{
new Employee { name = "Alex", number = "25860340" },
new Employee { name = "Tom", number = "94085345" }
};
var obj = new
{
employees = employees.ToDictionary(e => e.number)
};
string json = JsonConvert.SerializeObject(obj, Formatting.Indented);
Console.WriteLine(json);
That would give you this output, which is close to what you wanted:
{
"employees": {
"25860340": {
"name": "Alex",
"number": "25860340"
},
"94085345": {
"name": "Tom",
"number": "94085345"
}
}
}
If the employee number isn't actually unique, you could instead use each employee's position in the list as a key like this:
int i = 0;
var obj = new
{
employees = employees.ToDictionary(e => i++)
};
This would give you the following output instead:
{
"employees": {
"0": {
"name": "Alex",
"number": "25860340"
},
"1": {
"name": "Tom",
"number": "94085345"
}
}
}
I'm trying to get all the values of a certain attribute from a json array.
Considering the following json, I'm trying to get all the types e.g. iPhone,home
{
"firstName": "John",
"lastName" : "doe",
"age" : 26,
"address" :
{
"streetAddress": "naist street",
"city" : "Nara",
"postalCode" : "630-0192"
},
"phoneNumbers":
[
{
"type" : "iPhone",
"number": "0123-4567-8888"
},
{
"type" : "home",
"number": "0123-4567-8910"
}
]
}
I am using $.phoneNumbers[*].type which seems to work fine on online parsers
but when I'm using it in big query:
select json_extract(my_column,'$.phoneNumbers[*].type')
from my_table
I get:
JSONPath parse error at: [*].type
You can write a Javascript UDF to do the extraction:
SELECT JSON_EXTRACT('[1,2,3]', '$[*]') parsed
Error: Unsupported operator in JSONPath: *
UDF alternative:
#standardSQL
CREATE TEMPORARY FUNCTION parseJson(libs STRING)
RETURNS ARRAY<INT64>
LANGUAGE js AS """
try {
return JSON.parse(libs);
} catch (e) {
return [];
}
""";
SELECT parseJson('[1,2,3]') parsed
More complex example:
#standardSQL
CREATE TEMPORARY FUNCTION parseJson(libs STRING)
RETURNS ARRAY<STRUCT<x INT64, y INT64, z INT64>>
LANGUAGE js AS """
try {
return JSON.parse(libs);
} catch (e) {
return [];
}
""";
SELECT parseJson(JSON_EXTRACT('{"a":[{"x":1},{"y":2},{"z":3}]}', '$.a')) parsed
(inspired by: https://discuss.httparchive.org/t/javascript-library-detection/955)
json_extract cannot return REPEATED field, it can only do one match - hence no support for *
Yet another interesting (I hope) solution for BigQuery Standard SQL
Can be easily adjusted to whatever specific needs are
#standardSQL
CREATE TEMPORARY FUNCTION parseJson(data STRING)
RETURNS ARRAY<STRUCT<parent STRING, item STRING, key STRING, value STRING>>
LANGUAGE js AS """
x = JSON.parse(data); z = []; processKey(x, '');
function processKey(node, parent) {
if (parent !== '') {parent += '.'};
Object.keys(node).map(function(key) {
value = node[key].toString();
if (!value.startsWith('[object Object]')) {
var q = {}; var arr = parent.split('.');
q.parent = arr[0]; q.item = arr[1];
q.key = key; q.value = value;
z.push(q);
} else {
processKey(node[key], parent + key);
};
});
}; return z;
""";
WITH t AS (
SELECT """ {
"firstName": "John",
"lastName" : "doe",
"age" : 26,
"address" : {
"streetAddress": "naist street", "city" : "Nara", "postalCode" : "630-0192" },
"phoneNumbers": [
{ "type" : "iPhone", "number": "0123-4567-8888"},
{ "type" : "home", "number": "0123-4567-8910"},
{ "type" : "work", "number": "0123-4567-7777"}]
} """ AS info
)
SELECT parent, item, key, value FROM t, UNNEST(parseJson(info))
WHERE parent = 'phoneNumbers' AND key = 'type'