简体   繁体   中英

Get values from JSON Parse Object within Object; Dynamic Keys (JavaScript)

While I am able to properly parse this payload if I specify the keys via bracket notation, how could you parse it dynamically?

{
    "name": "Demo User",
    "birthday": "January 1st",
    "hobbies": 
        {
        "morning": "coding",
        "afternoon": "kewl kids stuff",
        "nighttime": "random"
    },
    "date": "June 25th"
}

What I mean by that is hobbies might not be there, or another field such as 'favorite foods' might exist, which could be an array, or an object.

This scenario has caused me a lot of frustration over the last couple months, and I'm trying to see if anyone can explain to me how to successfully parse it, dynamically.

I found a recursive 'walk' approach that is no longer erroring, but it's returning the first thing it comes across for every iteration.

var data = require("./demo.json");

//data = JSON.stringify(data);

function walk(obj) {
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      var val = obj[key];
      console.log(val);
      walk(val);
    }
  }
}

walk(data);

Basic on what I found, data is being imported as JSON, and if I stringify it, it becomes a string (obviously).

Default

{ name: 'Demo User',
  birthday: 'January 1st',
  hobbies:
   { morning: 'coding',
     afternoon: 'kewl kids stuff',
     nighttime: 'random' },
  date: 'June 25th' }

Stringify

{"name":"Demo User","birthday":"January 1st","hobbies":{"morning":"coding","afternoon":"kewl kids stuff","nighttime":"random"},"date":"June 25th"}

Both are similar, but the only difference on output is it spamming D X times (Being the first value, I'm thinking?) or spamming { X times (Being the first value of the string?

I've tried a much more basic approach of

var data = require("./demo.json");

for (var key in data){
    console.log(key + ':' + data[key]);
}

Which works fine, but as expected, hobbies is returning [object Object] since I am not iterating through it. I could go through hobbies , but again - I don't know if it will exist.

Welcome any input - Generic question, but a process that has caused me a lot of frustration on different projects over last few months.

UPDATE

My vagueness is causing, rightfully-so, confusion.

Let's say my objective is to turn this JSON Payload into a CSV. I need every key for the headers, and every value to be a row under said header. My issue is, as I iterate through it, I end up with the highest-level objects correctly converted. Then I end up with an object Object column with no data.

For this exact example, let's say my goal is to convert the JSON into

name, birthday, hobbies/morning, hobbies/afternoon, hobbies/nighttime, data
Demo User, January 1st, coding, kewl kids stuff, random, June 25th

Update # 2

Additional array variant.

I would expect

{
...
    "hobbies": 
        {
        "morning": "coding",
        "afternoon": "kewl kids stuff",
        "nighttime": "random"
    },
    ...
}

To output hobbies/morning, hobbies/afternoon, hobbies/nighttimes

I would expect

{
...
    "hobbies": ["coding", "kewl kids stuff", "coding"]
    ...
}

To output one column hobbies with quote-enclosed items "coding, kewl kids stuff, coding"

You can check the type of each value and decide what you want to do,

var data = require("./demo.json");

walk(obj){
    for (var key in data){
        if(type(data[key]) === "string"){
            console.log(key + ':' + data[key]);
        }
        else if(Array.isArray(data[key])){
            //its an array
        }
        else if(type(data[key]) === "object"){
            //its an object
            walk(data[key])       
        }
    }
}

The reason your walk function is spamming you with D or { is because it goes on infinite loop when it encountners an string,

function walk(obj) {
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      var val = obj[key];
      console.log(val);
      //here you need to check if it is an array or object, only then you should call walk
      //calling walk on string would send it on a infinite loop
      if(typeof(val) === "object"){
          walk(val);
      }
    }
  }
}

Try using this function ( Snippet at the end of the answer )

/**
 * 
 * @param {object} input 
 * @returns {Array<string>}
 */
function translateObject(input) {
    if (typeof input === "object" && input !== null) {
        if (input instanceof Array) {
            var result = '"';
            for (var index in input) {
                if (index) result += ", ";
                result += input[index];
            }
            return [result + '"'];
        } else {
            var data = "", result = "";
            for (var key in input) {
                if (key.includes(",")) {
                    throw new Error("Key cannot have a comma");
                }
                var val = translateObject(input[key]);
                if (val.length === 2) {
                    var titles = val[0].split(", ");
                    var textIndex = 0;
                    var size = 0;
                    for (var index in titles) {
                        var title = titles[index];
                        var titleVal = val[1].substring(textIndex, textIndex + title.length);
                        if (result) { result += ", "; data += ", "; }
                        textIndex += title.length + 2;
                        title = key + "/" + title;
                        size = Math.max(title.length, titleVal.length);
                        result += title + " ".repeat(size - title.length);
                        data += titleVal + " ".repeat(size - titleVal.length);
                    }
                } else if (val.length === 1) {
                    size = Math.max(val[0].length, key.length);
                    if (result) { result += ", "; data += ", "; }
                    result += key + " ".repeat(size - key.length);
                    data += val[0] + " ".repeat(size - val[0].length);
                }
            }
            return [result, data];
        }
    }
    return [input];
}

Here is a working example:

 var object = { "a": "1", "b": "2", "c": { "e": "3", "f": "4", "g": "5" }, "d": "6" }; function translateObject(input) { if (typeof input === "object" && input !== null) { if (input instanceof Array) { var result = '"'; for (var index in input) { if (index) result += ", "; result += input[index]; } return [result + '"']; } else { var data = "", result = ""; for (var key in input) { if (key.includes(",")) { throw new Error("Key cannot have a comma"); } var val = translateObject(input[key]); if (val.length === 2) { var titles = val[0].split(", "); var textIndex = 0; var size = 0; for (var index in titles) { var title = titles[index]; var titleVal = val[1].substring(textIndex, textIndex + title.length); if (result) { result += ", "; data += ", "; } textIndex += title.length + 2; title = key + "/" + title; size = Math.max(title.length, titleVal.length); result += title + " ".repeat(size - title.length); data += titleVal + " ".repeat(size - titleVal.length); } } else if (val.length === 1) { size = Math.max(val[0].length, key.length); if (result) { result += ", "; data += ", "; } result += key + " ".repeat(size - key.length); data += val[0] + " ".repeat(size - val[0].length); } } return [result, data]; } } return [input]; } function objectToCsv(object) { var result = translateObject(object); return result[0] + "\\n" + result[1]; } var csv = objectToCsv(object); document.querySelector("#console").innerText = csv; console.log(csv); 
 #console { font-family: Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace; white-space: pre; } span { color: darkgrey; } 
 <div id="console"></div> <span>Names were minified to fit result in one line so that it is easier to read</span> <span>Use this font family if you want all characters to have the same width</span> 

Maybe what you are describing is a case where one or more attributes of the object you are expecting is not present, or has no content (or members, if it is an array), and how to build your code on that basis.

It can't be entirely random, otherwise you wouldn't be talking about csv style output. So I will assume that your object contents are mostly there but occasionally you'll find something missing.

If it were me I would pre-process the objects loaded when you use JSON.parse() or whatever equivalent you use to convert the string to javascript objects. I would use something like jquery's $.extend to merge a perfectly formed object into my data, and array merging where the target is an array attribute. This would give me a consistent data model to code against.

In summary - you have to make the data the way you want it to be in order to be able to work with it without surprises.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM