Json output format from SQL Server 2017 - json

I have a stored procedure in SQL Server 2017 that outputs a result set as JSON. It works to output the set just fine, but I want it in a different format.
According to the MS documentation, the format it's sending out is as expected, but it seems counter-intuitive to me. I'm not JSON expert by any means but I've always assumed it to be a single object representation of a structure.
The query is:
SELECT
e.EventID AS 'Event.ID',
EventDate AS 'Event.Date',
ea.ActivityID AS 'Event.Activity.ID',
ea.CreateDate AS 'Event.Activity.Date',
ea.Notes AS 'Event.Activity.Notes'
FROM
Events e
JOIN
dbo.EventActivities ea ON e.EventID = ea.EventID
FOR JSON PATH
This returns an output of:
[
{"Event": {
"ID":"236",
"Date":"2019-03-01",
"Activity": {"ID": 10,
"Date":"2019-01-02T11:47:33.2800000",
"Notes":"Event created"}
}
},
{"Event": {
"ID":"236",
"Date":"2019-03-01",
"Activity": {"ID":20,
"Date":"2019-01-02T11:47:34.3933333",
"Notes":"Staff selected"}
}
},
{"Event": {
"ID":"236",
"Date":"2019-03-01",
"Activity": {"ID":20,
"Date":"2019-01-02T11:47:34.3933333",
"Notes":"Staff selected"}
}
}
]
When I format this manually (to visualise it better, it's giving me an array of 3 identical events, for each activity. This is consistent with what MS say in Format Nested JSON
I was expecting (or rather hoping) to see something like:
[
{
"Event": {
"ID": "236",
"Date": "2019-03-01",
"Activity": [
{
"ID": 10,
"Date": "2019-01-02T11:47:33.2800000",
"Notes": "Event created"
},
{
"ID": 20,
"Date": "2019-01-02T11:47:34.3933333",
"Notes": "Staff selected"
},
{
"ID": 20,
"Date": "2019-01-02T11:47:34.3933333",
"Notes": "Staff selected"
}
]
}
}
]
Is it possible to get an output formulated like this? or would this be invalid?

To start, you can test if a JSON String is valid with ISJSON. The expected output you indicated does not pass validation, but is close. It is missing a "[]" for the inner array.
However, I see where you were going with this. To better explain what I think the issue you are running into is, I am going to beautify the format of the output JSON from your query to match your expected JSON.
Original output as follows:
[
{"Event":
{"ID":"236","Date":"2019-03-01",
"Activity":{
"ID":10,"Date":"2019-01-02T11:47:33.2800000","Notes":"Event created"
}
}
},
{"Event":
{"ID":"236","Date":"2019-03-01",
"Activity":{
"ID":20,"Date":"2019-01-02T11:47:34.3933333","Notes":"Staff selected"}
}
},
{"Event":
{"ID":"236","Date":"2019-03-01",
"Activity":{
"ID":20,"Date":"2019-01-02T11:47:34.3933333","Notes":"Staff selected"
}
}
}
]
Based on your ideal format, a possible valid JSON string would be as follows:
{"Event":
[
{"ID":236,"Date":"2019-03-01",
"Activity":
[
{"ID":10,"Date":"2019-01-02T11:47:33.2800000","Notes":"Event created"},
{"ID":20,"Date":"2019-01-02T11:47:34.3933333","Notes":"Staff selected"},
{"ID":20,"Date":"2019-01-02T11:47:34.3933333","Notes":"Staff selected"}
]
}
]
}
You can achieve this by adjusting your table alias for the second table and using FOR JSON AUTO and ROOT. It will return an output with the "Event" attributes not repeated for each of its "EventActivities". For each "Event" it will put its related "EventActivities" into an array instead.
The following SQL will return the desired output:
SELECT [Event].EventID AS 'ID',
[Event].EventDate AS 'Date',
Activity.ActivityID AS 'ID',
Activity.CreateDate AS 'Date',
Activity.Notes AS 'Notes'
FROM #Events [Event]
JOIN #EventActivities Activity
ON [Event].EventID = Activity.EventID
FOR JSON AUTO, ROOT('Event')
The exact output for this will be as follows:
{"Event":[{"ID":236,"Date":"2019-03-01","Activity":[{"ID":10,"Date":"2019-01-02T11:47:33.2800000Z","Notes":"Event created"},{"ID":20,"Date":"2019-01-02T11:47:33.2800000Z","Notes":"Staff selected"},{"ID":20,"Date":"2019-01-02T11:47:33.2800000Z","Notes":"Staff selected"}]}]}
It will not return in a beautified format, but spaces and indentation can be added without compromising the fact that it is valid JSON, while also achieving the intended array requirements.

Your expected output is not a valid JSON. But, assuming you are referring Activity field as an array of activities, you can use nested queries.
SELECT
E.EventID AS 'Event.ID',
E.EventDate AS 'Event.Date',
(
SELECT
A.ActivityID AS 'ID',
A.CreateDate AS 'Date',
A.Notes AS 'Notes'
FROM dbo.EventActivities AS A
WHERE A.EventID = E.EventID
FOR JSON PATH
) AS 'Event.Activities'
FROM dbo.Events AS E
FOR JSON PATH

Related

Sort by nested fields in json in Power Automate

I'm trying to sort a JSON String in Power Automate by a nested field called "orderHint".
My JSON String looks like this:
[
{
"id": "5134",
"value": {
"isChecked": false,
"title": "This is another test",
"orderHint": "8585298133570680672PF"
},
"lastModifiedDateTime": "2022-12-23T11:06:28.4256622Z"
},
{
"id": "26576",
"value": {
"isChecked": true,
"title": "This is a test",
"orderHint": "8585498133570680672DE"
},
"lastModifiedDateTime": "2022-12-23T11:06:28.4256622Z"
}
]
When I'm trying to sort by "orderHint", I get an error:
"'The template language function 'sort' did not find the named sortField 'orderHint' on one or more objects in the array."
I'm using the following expression:
sort(variables('varArrayChecked'), 'value/orderHint')
Sorting by other fields works fine, e.g.:
sort(variables('varArrayChecked'), 'id')
Is there any way how I can sort by a nested field in a JSON String?
Thanks in advance!
You can use the Advanced Data Operations connector as it will do it for you in a single step.
The Flatten Object Array step is perfect for the payload you've provided.
You can see that it will take the data, flatten it and you have the ability to sort it on the way out (noting that the Array variable contains the exact JSON you provided in your question) ...
Note: Balance Output must be set to true in order for the sorting to occur.
Result
This is the resulting JSON order by orderHint ascending.
[
{
"id": "5134",
"lastModifiedDateTime": "2022-12-23T11:06:28",
"value/isChecked": false,
"value/orderHint": "8585298133570680672PF",
"value/title": "This is another test"
},
{
"id": "26576",
"lastModifiedDateTime": "2022-12-23T11:06:28",
"value/isChecked": true,
"value/orderHint": "8585498133570680672DE",
"value/title": "This is a test"
}
]
... and to show it in descending order (which is obvious, but simply change the sort order object value from Asc to Desc) ...
[
{
"id": "26576",
"lastModifiedDateTime": "2022-12-23T11:06:28",
"value/isChecked": true,
"value/orderHint": "8585498133570680672DE",
"value/title": "This is a test"
},
{
"id": "5134",
"lastModifiedDateTime": "2022-12-23T11:06:28",
"value/isChecked": false,
"value/orderHint": "8585298133570680672PF",
"value/title": "This is another test"
}
]

JSONPath to get multiple values from nested json

I have a nested JSON in a field that contains multiple important keys that I would like to retrieve as an array:
{
"tasks": [
{
"id": "task_1",
"name": "task_1_name",
"assignees": [
{
"id": "assignee_1",
"name": "assignee_1_name"
}
]
},
{
"id": "task_2",
"name": "task_2_name",
"assignees": [
{
"id": "assignee_2",
"name": "assignee_2_name"
},
{
"id": "assignee_3",
"name": "assignee_3_name"
}
]}]}
All the queries that I've tried so far fx ( $.tasks.*.assignees..id) and many others have returned
[
"assignee_1",
"assignee_2",
"assignee_3"
]
But what I need is:
[
["assignee_1"],
["assignee_2", "assignee_3"]
]
Is it possible to do with JSONPath or any script inside of it, without involving 3rd party tools?
The problem you're facing is that tasks and assignees are arrays. You need to use [*] instead of .* to get the items in the array. So your path should look like
$.tasks[*].assignees[*].id
You can try it at https://json-everything.net/json-path.
NOTE The output from my site will give you both the value and its location within the original document.
Edit
(I didn't read the whole thing :) )
You're not going to be able to get
[
["assignee_1"],
["assignee_2", "assignee_3"]
]
because, as #Tomalak mentioned, JSON Path is a query language. It's going to remove all structure and return only values.

How to filter in queries of wso2 sp complex json processing

I have a requirement to execute a complex json processing.
I have almost completed but struck with a logic of filtering the events based on an attribute. I know filtering on a normal basis but this is a case where tokenizeAsObject function is written next to the from statement.
Where and how he filtering part should be written in this case?
Your help is greatly appreciated.
I could find normal filtering for queries but couldn' find anything having tokenizing method in the querying/analysing data part of the code.
--THE CODE GOES HERE
#App:name('CompanyClientDetailingApp3')
#App:description('Description of the client details of the company')
#source(type='http', receiver.url='http://localhost:5005/clientDetails',
#map(type='json', #attributes(json = '$')
)
)
define stream CompanyClientStream3 (json string);
#sink(type = 'log',
#map(type = 'passThrough'))
define stream EachProjectStream3 (Client string, clientContractTerm string, projectName string, projectContractTerm int);
#info(name = 'clientProjectquery')
from CompanyClientStream3#json:tokenizeAsObject(json, '$.CompanyClients')
select json:getString(jsonElement, '$.Client') as Client, json:getString(jsonElement, '$.Invoice.ContractTerm') as clientContractTerm, json:getObject(jsonElement, '$.Invoice.Projects') as projectList
insert into EachClientProjectStream3;
#info(name = 'projectSttreamQuery')
from EachClientProjectStream3#json:tokenizeAsObject(projectList, "$")
select Client, clientContractTerm, json:getString(jsonElement, '$.ProjectName') as projectName, json:getInt(jsonElement, '$.ProjectTerm') as projectContractTerm
insert into EachProjectStream3;
Filtering is based on ProjectTerm. i.e. Projects having Projecterm > 5 years must be streamed out.
--Inputs for the same
{
"CompanyClients": [
{
"Client": "C1",
"Invoice": {
"ContractTerm": "5",
"Unit": "years",
"Projects": [
{"ProjectName":"C1P1", "ProjectTerm":"5", "TermUnit": "years"},
{"ProjectName":"C1P2", "ProjectTerm":"3", "TermUnit": "years"},
{"ProjectName":"C1P3", "ProjectTerm":"2", "TermUnit": "years"}
]
}
},
{
"Client": "C3",
"Invoice": {
"ContractTerm": "10",
"Unit": "years",
"Projects": [
{"ProjectName":"C3P1", "ProjectTerm":"8", "TermUnit": "years"},
{"ProjectName":"C3P2", "ProjectTerm":"5", "TermUnit": "years"},
{"ProjectName":"C3P3", "ProjectTerm":"6", "TermUnit": "years"}
]
}
}
]
}
Thanks,
Kaushik.
Output from the EachProjectStream3 can be filtered in the consecutive query,
from EachProjectStream3[projectContractTerm < 5]
slect *
insert into FilteredStream;

JMeter: Trying to verify two or more values in a randomly assigned json path

I have a JSON response that looks like this:
{
"results": [
{
"entityType": "PERSON",
"id": 679,
"graphId": "679.PERSON",
"details": [
{
"entityType": "PERSON",
"id": 679,
"graphId": "679.PERSON",
"parentId": 594,
"role": "Unspecified Person",
"relatedEntityType": "DOCUMENT",
"relatedId": 058,
"relatedGraphId": "058.DOCUMENT",
"relatedParentId": null
}
]
},
{
"entityType": "PERSON",
"id": 69678,
"graphId": "69678.PERSON",
"details": [
{
"entityType": "PERSON",
"id": 678,
"graphId": "678.PERSON",
"parentId": 594,
"role": "UNKNOWN",
"relatedEntityType": "DOCUMENT",
"relatedId": 145,
"relatedGraphId": "145.DOCUMENT",
"relatedParentId": null
}
]
}
The problem with this JSON response is that $.results[0] is not always the same, and it can have dozens of results. I know I can do individual JSON Assertion calls where I do the JSON with a wild card
$.results[*].details[0].entityType
$.results[*].details[0].relatedEntityType
etc
However I need to verify that both "PERSON" and "DOCUMENT" match correctly in the same path on one api call since the results come back in a different path each time.
Is there a way to do multiple calls in one JSON Assertion or am I using the wrong tool?
Thanks in advance for any help.
-Grav
I don't think JSON Assertion is flexible enough, consider switching to JSR223 Assertion where you have the full flexibility in terms of defining whatever pass/fail criteria you need.
Example code which checks that:
all attributes values which match $.results[*].details[0].entityType query are equal to PERSON
and all attributes values which match $.results[*].details[0].relatedEntityType are equal to DOCUMENT
would be:
def entityTypes = com.jayway.jsonpath.JsonPath.read(prev.getResponseDataAsString(), '$.results[*].details[0].entityType').collect().find { !it.equals('PERSON') }
def relatedEntityTypes = com.jayway.jsonpath.JsonPath.read(prev.getResponseDataAsString(), '$.results[*].details[0].relatedEntityType').collect().find { !it.equals('DOCUMENT') }
if (entityTypes.size() != 1) {
SampleResult.setSuccessful(false)
SampleResult.setResponseMessage('Entity type mismatch, one or more entries are not "PERSON" ' + entityTypes)
}
if (relatedEntityTypes.size() != 1) {
SampleResult.setSuccessful(false)
SampleResult.setResponseMessage('Entity type mismatch, one or more entries are not "DOCUMENT" ' + relatedEntityTypes)
}
More information:
SampleResult class JavaDoc
Groovy: Working with collections
Scripting JMeter Assertions in Groovy - A Tutorial

Accessing an Array Inside JSON with a Postgres Query

I have a table with a data_type of json that I need to query one of the properties inside of it.
This is what the data in the column looks like:
{
"id": 7008,
"access_links": [
{
"product_code": "PRODUCT-1",
"link": "https://some.url"
},
{
"product_code": "PRODUCT-2",
"link": "https://someOther.url"
}
],
"library_id": "2d1203db-75b3-43a5-947c-8555b48371db"
}
I need to be able to pull out and filter by the product_code nested inside of the access_links.
I can get one layer deep by using this query:
SELECT
courses.course_metadata -> 'access_links' as access_links
FROM
courses
This seems to get me into the column, but I can't query any further.
The output I receive from the query looks like:
[{"product_code":"PRODUCT-1","link":"https://some.url"},{"product_code":"PRODUCT-2","link":"https://someOther.url"}]
I've tried using the ->> and #>> operators, but they both complain about the array not starting with a {. Also worth noting that the column is a data type of JSON not JSONB, so the #> operator doesn't work.
What am I missing here?
Does this help?
select
json_array_elements (x->'access_links')->'product_code' as product_code
from
(select '{
"id": 7008,
"access_links": [
{
"product_code": "PRODUCT-1",
"link": "https://some.url"
},
{
"product_code": "PRODUCT-2",
"link": "https://someOther.url"
}
],
"library_id": "2d1203db-75b3-43a5-947c-8555b48371db"
}'::json x
) as v
;
product_code
"PRODUCT-1"
"PRODUCT-2"