Jacob Brauchler Jacob Brauchler - 5 months ago 14
JSON Question

Finding an object deep in a nested json object

I have the nested json object in the snippet below and want to find all occurrences of '$schema' and save the whole object that contains that schema value into another object. I tried using lodash filter but, was unsuccessful. Does anyone have any recommendations.



{
"element": "parseResult",
"content": [
{
"element": "category",
"meta": {
"classes": [
"api"
],
"title": "Test"
},
"attributes": {
"meta": [
{
"element": "member",
"meta": {
"classes": [
"user"
]
},
"content": {
"key": {
"element": "string",
"content": "FORMAT"
},
"value": {
"element": "string",
"content": "1A"
}
}
}
]
},
"content": [
{
"element": "category",
"meta": {
"classes": [
"resourceGroup"
],
"title": "Questions"
},
"content": [
{
"element": "resource",
"meta": {
"title": "Questions"
},
"attributes": {
"href": "/questions"
},
"content": [
{
"element": "transition",
"meta": {
"title": "List All Questions"
},
"content": [
{
"element": "httpTransaction",
"content": [
{
"element": "httpRequest",
"attributes": {
"method": "GET"
},
"content": []
},
{
"element": "httpResponse",
"attributes": {
"statusCode": "200",
"headers": {
"element": "httpHeaders",
"content": [
{
"element": "member",
"content": {
"key": {
"element": "string",
"content": "Content-Type"
},
"value": {
"element": "string",
"content": "application/json"
}
}
}
]
}
},
"content": [
{
"element": "dataStructure",
"content": [
{
"element": "Question List"
}
]
},
{
"element": "asset",
"meta": {
"classes": [
"messageBody"
]
},
"attributes": {
"contentType": "application/json"
},
"content": "[\n {\n \"question\": \"Favourite programming language?\",\n \"published_at\": \"2014-11-11T08:40:51.620Z\",\n \"url\": \"/questions/1\",\n \"choices\": [\n {\n \"choice\": \"Javascript\",\n \"url\": \"/questions/1/choices/1\",\n \"votes\": 2048\n }\n ]\n }\n]"
},
{
"element": "asset",
"meta": {
"classes": [
"messageBodySchema"
]
},
"attributes": {
"contentType": "application/schema+json"
},
"content": "{\n \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n \"type\": \"array\"\n}"
}
]
}
]
}
]
}
]
},
{
"element": "resource",
"meta": {
"title": "Question"
},
"attributes": {
"href": "/questions/{id}",
"hrefVariables": {
"element": "hrefVariables",
"content": [
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "id"
},
"value": {
"element": "number",
"content": 1234
}
}
}
]
}
},
"content": [
{
"element": "transition",
"meta": {
"title": "Retrieve Question"
},
"content": [
{
"element": "httpTransaction",
"content": [
{
"element": "httpRequest",
"attributes": {
"method": "GET"
},
"content": []
},
{
"element": "httpResponse",
"attributes": {
"statusCode": "200",
"headers": {
"element": "httpHeaders",
"content": [
{
"element": "member",
"content": {
"key": {
"element": "string",
"content": "Content-Type"
},
"value": {
"element": "string",
"content": "application/json"
}
}
}
]
}
},
"content": [
{
"element": "dataStructure",
"content": [
{
"element": "Question"
}
]
},
{
"element": "asset",
"meta": {
"classes": [
"messageBody"
]
},
"attributes": {
"contentType": "application/json"
},
"content": "{\n \"question\": \"Favourite programming language?\",\n \"published_at\": \"2014-11-11T08:40:51.620Z\",\n \"url\": \"/questions/1\",\n \"choices\": [\n {\n \"choice\": \"Javascript\",\n \"url\": \"/questions/1/choices/1\",\n \"votes\": 2048\n }\n ]\n}"
},
{
"element": "asset",
"meta": {
"classes": [
"messageBodySchema"
]
},
"attributes": {
"contentType": "application/schema+json"
},
"content": "{\n \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n \"type\": \"object\",\n \"properties\": {\n \"question\": {\n \"type\": \"string\"\n },\n \"published_at\": {\n \"type\": \"string\"\n },\n \"url\": {\n \"type\": \"string\"\n },\n \"choices\": {\n \"type\": \"array\"\n }\n },\n \"required\": [\n \"question\",\n \"published_at\",\n \"url\",\n \"choices\"\n ]\n}"
}
]
}
]
}
]
}
]
}
]
},
{
"element": "category",
"meta": {
"classes": [
"dataStructures"
]
},
"content": [
{
"element": "dataStructure",
"content": [
{
"element": "object",
"meta": {
"id": "Question"
},
"content": [
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "question"
},
"value": {
"element": "string",
"content": "Favourite programming language?"
}
}
},
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "published_at"
},
"value": {
"element": "string",
"content": "2014-11-11T08:40:51.620Z"
}
}
},
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "url"
},
"value": {
"element": "string",
"content": "/questions/1"
}
}
},
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "choices"
},
"value": {
"element": "array",
"content": [
{
"element": "Choice"
}
]
}
}
}
]
}
]
},
{
"element": "dataStructure",
"content": [
{
"element": "object",
"meta": {
"id": "Choice"
},
"content": [
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "choice"
},
"value": {
"element": "string",
"content": "Javascript"
}
}
},
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "url"
},
"value": {
"element": "string",
"content": "/questions/1/choices/1"
}
}
},
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "votes"
},
"value": {
"element": "number",
"content": 2048
}
}
}
]
}
]
},
{
"element": "dataStructure",
"content": [
{
"element": "array",
"meta": {
"id": "Question List"
},
"content": [
{
"element": "Question"
}
]
}
]
}
]
}
]
}
]
}





I tried this as a solution.

function getObject(theObject) {
var result = null;
if(theObject instanceof Array) {
for(var i = 0; i < theObject.length; i++) {
result = getObject(theObject[i]);
}
}
else
{
for(var prop in theObject) {
console.log(prop + ': ' + theObject[prop]);
if(prop == '$schema') {
if(theObject[prop] == 'http://json-schema.org/draft-04/schema#') {
return theObject;
}
}
if(theObject[prop] instanceof Object || theObject[prop] instanceof Array)
result = getObject(theObject[prop]);
}
}
return result;
}

var result = getObject(json);

Answer

Jacob,

The problem is in your JSON itself. Your json is a key/value pair. Keys can be strings (surrounded with "quotes") to enable properties that have spaces or special characters. e.g.

var obj = { "my property": "my value" };
var val = obj["my property"] // "my value";

However your values should only have quotes around them if they are strings, otherwise it won't get parsed to an object - it will remain in string representation.

var obj = { "foo" : "{ \"bar\" : \"my value\" }" } // notice I had to escape the strings with backslashes \
var val = obj.foo.bar // error because foo is a string with the value "{ "bar" : "my value" }"

In your json, the value of the content property is a string:

"content": "{\n  \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n  \"type\": \"array\"\n}"

The fact that you needed to escape your quotes with \ tells you this as well. If you expect the content to be parsed the first time, then it should look something like this:

"content": { "$schema": "http://json-schema.org/draft-04/schema#", "type": "array" }

If you can't change this, then you can call an eval() or a JSON.parse() on the content to parse the string to an object (this can be unsafe if your content is coming from the users).

Edit: Because you cannot change the JSON coming in, I created a function called deepParseObject. It's by no means error-proof, but I think it will work for your situation:

function deepParseObject(theObject) {
  for(var prop in theObject) {
      if(typeof theObject[prop] === "string" && (theObject[prop].indexOf('{') == 0 || theObject[prop].indexOf('[') == 0)) {
          theObject[prop] = JSON.parse(theObject[prop]);
      }
      if(theObject[prop] instanceof Object || theObject[prop] instanceof Array)
          deepParseObject(theObject[prop]);
  }
}

What it does is parse the content properties if their values are strings and start with a { or [. After that you can call my original getObject function. Here is the plunker. https://plnkr.co/edit/h06wuAxZkxhwCAho3OK6

I call deepParseObject in the data.js and I'm only using AngularJS to output the results... and because I like Angular :).

enter image description here