Getting Data In

Extracting data from complicated JSON, match a value

CanadianTrevorS
New Member

I have JSON in the following format:

[
    {
      "nameValues": [], 
      "offeringId": "a"
    }, 
    {
      "nameValues": [
        {
          "name": "key1", 
          "value": "true"
        }, 
        {
          "name": "key2", 
          "value": "value2"
        }
      ], 
      "offeringId": "b"
    }
]

I am looking to match a create a field with the value of one of the one the name value pairs, that matches the offeringId=b. The output being key1=true. All the keyvalue pairs in nameValues would obviously also suffice.

I've struggled with spath, but not sure that you can select a specific offeringId=b like you can do in xpath. Or would I be better off attempting this via regex?

Any help would be much appreciated.

Tags (1)
0 Karma
1 Solution

DalJeanis
Legend

Okay, I lied, it can be done. It just has to be done in steps.

Here's the whole code, assuming you were looking for the value of "key2" for offeringId="b". A full explanation of what I've done here follows that.

| makeresults | eval source="[{\"nameValues\":[],\"offeringId\":\"a\"},{\"nameValues\":[{\"name\":\"key1\",\"value\":\"true\"},{\"name\":\"key2\",\"value\":\"value2\"}],\"offeringId\": \"b\"}]"
 | spath input=source path="{}"
 | rename "{}" as mystuff
 | mvexpand mystuff 
 | spath input=mystuff
 | table source mystuff offeringId nameValues{}.name nameValues{}.value
 | where offeringId="b"
 | rename nameValues{}.name as MyName, nameValues{}.value as MyValue
 | eval MyNameValue=mvzip(MyName, MyValue,"=")
 | table offeringId MyNameValue
 | mvexpand MyNameValue
 | where match(MyNameValue,"key2")

This just creates the test data.

 | makeresults | eval source="[{\"nameValues\":[],\"offeringId\":\"a\"},{\"nameValues\":[{\"name\":\"key1\",\"value\":\"true\"},{\"name\":\"key2\",\"value\":\"value2\"}],\"offeringId\": \"b\"}]"

This spath with a path set to {} will extract the data at that first, highest level.

 | spath input=source path="{}"
 | rename "{}" as mystuff

Now we use mvexpand to break up the JSON into individual transactions at that same, highest level, and then run spath again on the result. At this level, the key/value pairs are connected to the offeringId. We throw away everything that wasn't offeringId = "b".

| mvexpand mystuff 
| spath input=mystuff
| table source mystuff offeringId nameValues{}.name nameValues{}.value
| where offeringId="b"

Now, we change the name from the hideous JSON format to a plan name, and mvzip together the related name/value fields. I'm formatting them here as a single field where the data looks like "key2=value2".

| rename nameValues{}.name as MyName, nameValues{}.value as MyValue
| eval MyNameValue=mvzip(MyName, MyValue,"=")
| table offeringId MyNameValue

Finally, we expand again, and throw away all the rows that don't contain the key value we were looking for.

| mvexpand MyNameValue
| where match(MyNameValue,"key2")

View solution in original post

0 Karma

DalJeanis
Legend

Okay, I lied, it can be done. It just has to be done in steps.

Here's the whole code, assuming you were looking for the value of "key2" for offeringId="b". A full explanation of what I've done here follows that.

| makeresults | eval source="[{\"nameValues\":[],\"offeringId\":\"a\"},{\"nameValues\":[{\"name\":\"key1\",\"value\":\"true\"},{\"name\":\"key2\",\"value\":\"value2\"}],\"offeringId\": \"b\"}]"
 | spath input=source path="{}"
 | rename "{}" as mystuff
 | mvexpand mystuff 
 | spath input=mystuff
 | table source mystuff offeringId nameValues{}.name nameValues{}.value
 | where offeringId="b"
 | rename nameValues{}.name as MyName, nameValues{}.value as MyValue
 | eval MyNameValue=mvzip(MyName, MyValue,"=")
 | table offeringId MyNameValue
 | mvexpand MyNameValue
 | where match(MyNameValue,"key2")

This just creates the test data.

 | makeresults | eval source="[{\"nameValues\":[],\"offeringId\":\"a\"},{\"nameValues\":[{\"name\":\"key1\",\"value\":\"true\"},{\"name\":\"key2\",\"value\":\"value2\"}],\"offeringId\": \"b\"}]"

This spath with a path set to {} will extract the data at that first, highest level.

 | spath input=source path="{}"
 | rename "{}" as mystuff

Now we use mvexpand to break up the JSON into individual transactions at that same, highest level, and then run spath again on the result. At this level, the key/value pairs are connected to the offeringId. We throw away everything that wasn't offeringId = "b".

| mvexpand mystuff 
| spath input=mystuff
| table source mystuff offeringId nameValues{}.name nameValues{}.value
| where offeringId="b"

Now, we change the name from the hideous JSON format to a plan name, and mvzip together the related name/value fields. I'm formatting them here as a single field where the data looks like "key2=value2".

| rename nameValues{}.name as MyName, nameValues{}.value as MyValue
| eval MyNameValue=mvzip(MyName, MyValue,"=")
| table offeringId MyNameValue

Finally, we expand again, and throw away all the rows that don't contain the key value we were looking for.

| mvexpand MyNameValue
| where match(MyNameValue,"key2")
0 Karma

CanadianTrevorS
New Member

Wow, this was a lot of steps, I am very grateful, I made a slight tweak:

after the

rename nameValues{}.name as MyName, nameValues{}.value as MyValue

I added

| search MyName=keyNameIwasLookingFor AND MyValue=valueIWasLookingFor | table MyName, MyValue

and was able to limit to only include entries that contained keyNameIwasLookingFor=valueIWasLookingFor

0 Karma

DalJeanis
Legend

Okay, I don't think that's right. If you already knew what the value was, then what was the point of checking the json?

0 Karma

DalJeanis
Legend

THIS ANSWER, THOUGH SENSIBLE, WAS WRONG. SEE THE OTHER ONE.

Unfortunately, since the offering containers are unnamed, there is no way to differentiate which offering a particular key came from

| makeresults | eval source="[{\"nameValues\":[],\"offeringId\":\"a\"},{\"nameValues\":[{\"name\":\"key1\",\"value\":\"true\"},{\"name\":\"key2\",\"value\":\"value2\"}],\"offeringId\": \"b\"}]"
| spath input=source 
| table source  {}.nameValues{}.name {}.nameValues{}.value {}.offeringId

outputs something like this

source     {}.nameValues{}.name {}.nameValues{}.value   {}.offeringId

(the JSON)  key1                   true                    a

            key2                   value2                  b   

Since there is no cardinality that relates the OfferingId with the keys, there's nothing there to help us pull them out, as far as I can see, in the current JSON structure.

It might be seen more clearly in this example, where there are five keys for the two OfferingIds

| makeresults | eval source=
"[{\"nameValues\":[
       {\"name\":\"key3\",\"value\":\"maybe\"},
       {\"name\":\"key7\",\"value\":\"idunno\"},
       {\"name\":\"key5\",\"value\":\"letmecheckmynotes\"}
       ],
   \"offeringId\":\"a\"},
  {\"nameValues\":[
       {\"name\":\"key1\",\"value\":\"true\"},
       {\"name\":\"key2\",\"value\":\"value2\"}
       ],
   \"offeringId\": \"b\"}]"
 | spath input=source 
 | table source  {}.nameValues{}.name {}.nameValues{}.value {}.offeringId
0 Karma
Get Updates on the Splunk Community!

.conf24 | Registration Open!

Hello, hello! I come bearing good news: Registration for .conf24 is now open!   conf is Splunk’s rad annual ...

ICYMI - Check out the latest releases of Splunk Edge Processor

Splunk is pleased to announce the latest enhancements to Splunk Edge Processor.  HEC Receiver authorization ...

Introducing the 2024 SplunkTrust!

Hello, Splunk Community! We are beyond thrilled to announce our newest group of SplunkTrust members!  The ...