Eiffel library: JSON_OBJECT.item and common method to get content - json

If I want to have the content value as string of a JSON_OBJECT.item ("key") without having to
some_json_value_as_string: STRING
do
if attached {JSON_STRING} l_json_o as l_s then
Result := l_s.unescaped_string_8
elseif attached {JSON_NUMBER} l_json_o as l_n then
Result := l_n.item.out
else
check
you_forgot_to_treat_a_case: False
end
end
end
for a json object like
{
| | "datasource_name": "DODBC",
| | "datasource_username": "dev_db_usr",
| | "datasource_password": "somePassword",
| | "ewf_listening_port": 9997,
| | "log_file_path": "/var/log/ewf_app.log",
| | "default_selected_company": 1,
| | "default_selected_branch": 1,
| | "default_selected_consumption_sector": 1,
| | "default_selected_measuring_point": 1,
| | "default_selected_charge_unit": -1
| }
the {JSON_VALUE}.representation with io.putstring is:
datasource_username=dev_db_usr
and not the value only!!!
is there a way to do that? I didn't find intuitive the different methods of JSON_VALUE: values as the out method gives the class and pointer address, which is really far from a string representation of the associated json object for me...

The feature {JSON_VALUE}.representation is the string representation of the Current JSON value.
Ok, but if you have jo: JSON_OBJECT and then suppose you have datasource_username_key: STRING = "datasource_username"
You can do
if attached jo.item (datasource_username_key) as l_value then
print (l_value.representation)
end

Related

MySQL many-many JSON aggregation merging duplicate keys

I'm having trouble returning a JSON representation of a many-many join. My plan was to encode the columns returned using the following JSON format
{
"dog": [
"duke"
],
"location": [
"home",
"scotland"
]
}
This format would handle duplicate keys by aggregating the results in a JSON array, howver all of my attempts at aggregating this structure so far have just removed duplicates, so the arrays only ever have a single element.
Tables
Here is a simplified table structure I've made for the purposes of explaining this query.
media
| media_id | sha256 | filepath |
| 1 | 33327AD02AD09523C66668C7674748701104CE7A9976BC3ED8BA836C74443DBC | /photos/cat.jpeg |
| 2 | 323b5e69e72ba980cd4accbdbb59c5061f28acc7c0963fee893c9a40db929070 | /photos/dog.jpeg |
| 3 | B986620404660DCA7B3DEC4EFB2DE80C0548AB0DE243B6D59DA445DE2841E474 | /photos/dog2.jpeg |
| 4 | 1be439dd87cd87087a425c760d6d8edc484f126b5447beb2203d21e09e2a8f11 | /photos/balloon.jpeg |
media_metdata_labels_has_media (for many-many joins)
| media_metadata_labels_label_id | media_media_id |
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 1 | 2 |
| 4 | 2 |
| 5 | 2 |
| 1 | 3 |
| 6 | 3 |
| 7 | 3 |
| 8 | 4 |
| 9 | 4 |
media_metadata_labels
| label_id | label_key | label_value |
| 2 | cat | lily |
| 4 | dog | duke |
| 6 | dog | rex |
| 1 | pet size | small |
| 3 | location | home |
| 7 | location | park |
| 8 | location | scotland |
| 9 | location | sky |
| 5 | location | studio |
My current attempt
My latest attempt at querying this data uses JSON_MERGE_PRESERVE with two arguments, the first is just an empty JSON object and the second is an invalid JSON document. It's invalid because there are duplicate keys, but I was hoping that JSON_MERGE_PRESERVE would merge them. It turns out JSON_MERGE_PRESERVE will only merge duplicates if they're not in the same JSON argument.
For example, this won't merge two keys
SET #key_one = '{}';
SET #key_two = '{"location": ["home"], "location": ["scotland"]}';
SELECT JSON_MERGE_PRESERVE(#key_one, #key_two);
-- returns {"location": ["scotland"]}
but this will
SET #key_one = '{"location": ["home"] }';
SET #key_two = '{"location": ["scotland"]}';
SELECT JSON_MERGE_PRESERVE(#key_one, #key_two);
-- returns {"location": ["home", "scotland"]}
So anyway, here's my current attempt
SELECT
m.media_id,
m.filepath,
JSON_MERGE_PRESERVE(
'{}',
CAST(
CONCAT(
'{',
GROUP_CONCAT(CONCAT('"', l.label_key, '":["', l.label_value, '"]')),
'}'
)
AS JSON)
)
as labels
FROM media AS m
LEFT JOIN media_metadata_labels_has_media AS lm ON lm.media_media_id = m.media_id
LEFT JOIN media_metadata_labels AS l ON l.label_id = lm.media_metadata_labels_label_id
GROUP BY m.media_id, m.filepath
-- HAVING JSON_CONTAINS(labels, '"location"', CONCAT('$.', '"home"')); -- this would let me filter on labels one they're in the correct JSON format
After trying different combinations of JSON_MERGE, JSON_OBJECTAGG, JSON_ARRAYAGG, CONCAT and GROUP_CONCAT this still leaves me scratching my head.
Disclaimer: Since posting this question I've started using mariadb instead of oracle MySQL. The function below should work for MySQL too, but in case it doesn't then any changes required will likely be small syntax fixes.
I solved this by creating a custom aggregation function
DELIMITER //
CREATE AGGREGATE FUNCTION JSON_LABELAGG (
json_key TEXT,
json_value TEXT
) RETURNS JSON
BEGIN
DECLARE complete_json JSON DEFAULT '{}';
DECLARE current_jsonpath TEXT;
DECLARE current_jsonpath_value_type TEXT;
DECLARE current_jsonpath_value JSON;
DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN complete_json;
main_loop: LOOP
FETCH GROUP NEXT ROW;
SET current_jsonpath = CONCAT('$.', json_key); -- the jsonpath to our json_key
SET current_jsonpath_value_type = JSON_TYPE(JSON_EXTRACT(complete_json, current_jsonpath)); -- the json object type at the current path
SET current_jsonpath_value = JSON_QUERY(complete_json, current_jsonpath); -- the json value at the current path
-- if this is the first label value with this key then place it in a new array
IF (ISNULL(current_jsonpath_value_type)) THEN
SET complete_json = JSON_INSERT(complete_json, current_jsonpath, JSON_ARRAY(json_value));
ITERATE main_loop;
END IF;
-- confirm that an array is at this jsonpath, otherwise that's an exception
CASE current_jsonpath_value_type
WHEN 'ARRAY' THEN
-- check if our json_value is already within the array and don't push a duplicate if it is
IF (ISNULL(JSON_SEARCH(JSON_EXTRACT(complete_json, current_jsonpath), "one", json_value))) THEN
SET complete_json = JSON_ARRAY_APPEND(complete_json, current_jsonpath, json_value);
END IF;
ITERATE main_loop;
ELSE
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Expected JSON label object to be an array';
END CASE;
END LOOP;
RETURN complete_json;
END //
DELIMITER ;
and editing my query to use it
SELECT
m.media_id,
m.filepath,
JSON_LABELAGG(l.label_key, l.label_value) as labels
FROM media AS m
LEFT JOIN media_metadata_labels_has_media AS lm ON lm.media_media_id = m.media_id
LEFT JOIN media_metadata_labels AS l ON l.label_id = lm.media_metadata_labels_label_id
GROUP BY m.media_id, m.filepath

Parse JSON Array where each member has different schema but same general structure

I have a JSON data feed coming into SQL Server 2016. One of the attributes I must parse contains a JSON array. Unfortunately, instead of implementing a key/value design, the source system sends each member of the array with a different attribute name. The attribute names are not known in advance, and are subject to change/volatility.
declare #json nvarchar(max) =
'{
"objects": [
{"foo":"fooValue"},
{"bar":"barValue"},
{"baz":"bazValue"}
]
}';
select * from openjson(json_query(#json, 'strict $.objects'));
As you can see:
element 0 has a "foo" attribute
element 1 has a "bar" attribute
element 2 has a "baz" attribute:
+-----+--------------------+------+
| key | value | type |
+-----+--------------------+------+
| 0 | {"foo":"fooValue"} | 5 |
| 1 | {"bar":"barValue"} | 5 |
| 2 | {"baz":"bazValue"} | 5 |
+-----+--------------------+------+
Ideally, I would like to parse and project the data like so:
+-----+---------------+----------------+------+
| key | attributeName | attributeValue | type |
+-----+---------------+----------------+------+
| 0 | foo | fooValue | 5 |
| 1 | bar | barValue | 5 |
| 2 | baz | bazValue | 5 |
+-----+---------------+----------------+------+
Reminder: The attribute names are not known in advance, and are subject to change/volatility.
select o.[key], v.* --v.[key] as attributeName, v.value as attributeValue
from openjson(json_query(#json, 'strict $.objects')) as o
cross apply openjson(o.[value]) as v;

In Karate DSL, how can I use the replace text for other data types such as int, float, Big, etc.?

I found the below example on github.
def text = 'hello world bye'
replace text
| token | value |
| one | 'cruel' |
| two | 'good' |
match text == 'hello cruel world good bye'
What If the value I want to replace can only accept integers or other data types? For example,
replace text
| token | value|
| hours | 90 |
| price | 123.45 |
| quantity | 999999999999 |
I was not able to put the token inside another file because the json validator does not like the <> without the double quotes. any suggestions?
Replace is meant for text not JSON, read the doc carefully please. First, there is no problem with numbers and replace:
* def text = 'hello <name> how many <hours>'
* replace text
| token | value |
| name | 'John' |
| hours | 200 |
* match text == 'hello John how many 200'
Now, if you are trying to fiddle with JSON, just use the set keyword.
* def json = { hello: '', hours: null }
* set json
| path | value |
| hello | 'John' |
| hours | 200 |
* match json == { hello: 'John', hours: 200 }
Note that the above would work even if you omit the first line. Also refer to embedded expressions as another way to substitute values in JSON, refer to the doc.

How to embed an Example table values into Json in Scenario Outline in Gherkin

Scenario Outline: I have an audit type <type>
When a request is received
"""
{
"elements":{
"type":<type>,
"id":"sku-1"
}
"""
Examples:
| type |
| test1 |
| test2 |
| test3 |
How to solve the above problem to replace with the values given in Examples?
I got it worked with simple change
Scenario Outline: I have an audit type <type>
When a request is received
"""
{
"elements":{
"type":'<type>',
"id":"sku-1"
}
"""
Examples:
| type |
| test1 |
| test2 |
| test3 |
Might be better not to use Doc String for JSON. Instead try this:
Scenario Outline: I have an audit type
When a request is received of "<type>"
Examples:
| type |
| test1 |
| test2 |
| test3 |
Sorry, the response in in Ruby, not familiar with Java conversion from String to JSON object or Hashtable. I did however find this resource that might help to manage handle the JSON:
In Ruby:
When(/^a request is received of "([^"]*)"$/) do |type|
json = eval("{'elements':{'type':'#{type}','id':'sku-1'}}")
end
First time this runs, json will be: {'elements':{'type':'test1','id':'sku-1'}}

grails - findBy highest id AND another criteria

I've looked a bunch of answers to this question here on SO and elsewhere but all I can track down is cases where people just want to find the highest id, the max dateCreated or the latest db entry but what I want to do is retrieve the latest object created that also matches another criteria. My domain class has the following properties: id, number, company, type, dateCreated and content. The company property can only be set to 'OYG' or 'BAW' and the number property is an auto incrementing int. What I want to do is retrieve the record with the highest number that also has its company property set to 'OYG' or 'BAW`.
So here's an example:
+----------------------------------------------------------+
| id | number | company | type | dateCreated | content |
+----------------------------------------------------------+
| 1 | 0 | OYG | TsAndCs | 15/09/2016 | stuff |
| 2 | 0 | BAW | TsAndCs | 15/09/2016 | stuff |
| 3 | 1 | OYG | TsAndCs | 16/09/2016 | stuff |
| 4 | 2 | OYG | TsAndCs | 17/09/2016 | stuff |
| 5 | 1 | BAW | TsAndCs | 16/09/2016 | stuff |
+----------------------------------------------------------+
I want to say def doc = Document.findByHighestNumberAndCompany('OYG') then it should bring back the object with id 4. def doc = Document.findByHighestNumberAndCompany('BAW') should bring back id 5's object, etc.
Any help would be appreciated. Thanks!
Despite Joshua Moore gave you a good solution, there is another simplier in one line.
MyDomain.findAllByCompany(company, [sort: 'number', order: 'desc', limit: 1])?.first()
Should be easy enough if you order by the number in descending order, and limit your results to one. So perhaps something like this?
String companyName = 'OYG'
def results = MyDomain.createCriteria().list() {
eq("company", companyName)
maxResults(1)
order("number", "desc")
}
println results[0].id // will print 4
Using this approach you could create a named query so you can pass the company name as a parameter.