简体   繁体   中英

Creating and Populating JSON Object with jQuery via Loops

I am trying to create a JSON Object dynamically using some multiple .each() loops. I tried using .push() but I was only able to get the first "tier" of the JSON object (the first array) populated.

The JS parses through an Excel Spreadsheet (2003/XML) file and needs to output this JSON object "styles" so I can use it to generate CSS on the page(s). So basically, we save an Excel XML Spreadsheet, my JS uses AJAX to "GET" it, then parses through it extracting the styles (and the worksheets/their data). The extraction process should then "dynamically" create this JSON object to be used elsewhere in the JS file.

Here is what I need the JSON to be after the various functions and loops are complete (unless there is a better structure or a structure that makes more sense for my situation)...

var styles = [
  "Default": {
    "Name": "Normal",
    "Style": {
      "Alignment": {
        "Vertical": "Bottom"
      },
      "Font": {
        "Name": "Calibri",
        "Family": "Swiss",
        "Size": "11",
        "Color": "#000000"
      }
    }
  },
  "s57": {
    "Name": "Hyperlink",
    "Style": {
      "Font": {
        "Name": "Calibri",
        "Family": "Swiss",
        "Size": "11",
        "Color": "#0066CC",
        "Underline": "Single"
      }
    }
  }
]

MY JS (so far)

var styles = []

$(xml).find('Style').each(function(){

  var style = {}

  var id = $(this).attr('ss:ID');

  var type = $(this).children();

  type.each(function() {

    $.each(this.attributes, function() {

      if (this.specified) {
        style[this.name] = this.value;
      }

    });

  });

  styles.push(style);

  console.log(styles);

});

It doesn't work so well. Once I added style[this.name] = this.value , the console was showing a bunch of "X: Object" entries.

SO, how can I generate a JSON object "dynamically" using the .each() and $.each() loops above?

Thanks in advance!

PS I have searched quite a bit to try to find an answer to this already. I have found bits and pieces on how to do some of this, but none that populated the object intelligently...

EDIT:

Here is the XML file that I am "parsing":

.XML Victim

UPDATE:

I'm getting closer with this:

// Create JSON Object "styles"
var styles = [];

$(xml).find('Style').each(function(){

  var style = {};

  var id = $(this).attr('ss:ID');
  var type = $(this).children();

  type.each(function() {

    $.each(this.attributes, function() {

      if (this.specified) {
        style[this.name] = this.value;
      }

    });

  });

  //styles[id] = style;
  styles.push(style);

});

console.log(styles);

$(document).ready(function(){
  $('body').append('<div class="json-output"/>');
  $('.json-output').append(JSON.stringify(styles));
});

** JSON.stringify(styles) is now outputting this with the above scripting

[
{
"ss:Vertical":"Bottom",
"ss:FontName":"Calibri",
"x:Family":"Swiss",
"ss:Size":"11",
"ss:Color":"#000000"
},
"ss:FontName":"Calibri",
"x:Family":"Swiss",
"ss:Size":"11",
"ss:Color":"#0066CC",
"ss:Underline":"Single"
},
{
"ss:Horizontal":"Left",
"ss:Vertical":"Center",
"ss:Indent":"1"
},    {
"ss:Vertical":"Center",
"ss:WrapText":"1",
"ss:FontName":"Calibri",
"x:Family":"Swiss",
"ss:Size":"11",
"ss:Color":"#000000",
"ss:Bold":"1"
},
{
"ss:Vertical":"Center",
"ss:WrapText":"1",
"ss:FontName":"Calibri",
"x:Family":"Swiss",
"ss:Size":"11",
"ss:Color":"#008000",
"ss:Bold":"1"
},
{
"ss:Vertical":"Center",
"ss:WrapText":"1"
},
{
"ss:Vertical":"Center",
"ss:WrapText":"1",
"ss:FontName":"Calibri",
"x:Family":"Swiss",
"ss:Size":"11",
"ss:Color":"#808080",
"ss:Bold":"1",
"ss:Pattern":"Solid"
},
{
"ss:Horizontal":"Left",
"ss:Vertical":"Bottom"
},
{
"ss:Horizontal":"Left",
"ss:Vertical":"Center",
"ss:WrapText":"1",
"ss:Format":"0"
},
{
"ss:Horizontal":"Left",
"ss:Vertical":"Center",
"ss:Indent":"1",
"ss:WrapText":"1"
}
]
...

What you need is

  var root = $(xml),
      styles = {},
      all = root.find('Style');

  all.each(function(index, item){
    var self = $(this),
        id = self.attr('ss:ID'),
        type = self.children(),
        style = {};

    styles[id] = style;

    type.each(function(index, item){
      var attributes = item.attributes;
      if (attributes.length){
        style[ item.nodeName ] = {};
        for (var i = 0, l = attributes.length; i < l; i++){
          var attribute = attributes[i],
              attrName = attribute.nodeName;

          if (attrName.indexOf(':') > -1){
            attrName = attrName.split(':')[1];
          }
          style[ item.nodeName ][ attrName ] = attribute.value;
        }
      }
    });
  });

It will return an object as you describe ( although correct as your target variable is wrong because it is an array but has the structure of an object )

Thanks for posting the XML file. Instead of using this.name, reference this.localName to get the non-XML namespaced name - the actual node name. That will return an array of Objects with the styles defined in them. Additionally, since you are using an array, you won't be able to set the name/ID of the style. You need to change "styles" to an object and you can set the name/ID the same way you do with each style.

EDIT : Updated with my full code as I left something out that might have really helped you.

Try:

$(function () {
    $.get('/StackTest.xml', function (xml) {  // i had it locally saved in my sandbox
        var styles = {};

        $(xml).find('Style').each(function () {
            var style = {};
            var id = $(this).attr('ss:ID');
            var type = $(this).children();

            type.each(function () {
                $.each(this.attributes, function () {
                    if (this.specified) {
                        style[this.localName] = this.value;
                    }
                });
            });
            styles[id] = style;
        });
        console.log(styles);
        console.log(JSON.stringify(styles)); // see what it looks like as string - notice no array.
    });
});

So here is what I ended up with...

It allow me to reference things like styles['s57'].FontName (the font for all cells with the s57 style for example) .

// Get Excel Spreadsheet (2003/XML Only!)
$.ajax({
  type: "GET",
  url: '/data/KH-3140300109.xml',
  dataType: "xml",
  success: function(xml) {
    console.log('%cgetExcel() was successful!', 'color:green; font-weight:bold;');
    createStyles(xml);
  },
  error: function(){
    console.error('getData() was unsuccessful');
  }
});

function createStyles(xml) {

  // Create JSON object "styles"
  var styles = {}

  $(xml).find('Style').each(function(){

    var id = $(this).attr('ss:ID');
    var name = $(this).attr('ss:Name');
    var type = $(this).children();

    // Create JSON object "style"
    var style = {};

    // Identify the style
    styles[id] = style;

    // Log its name
    style['Name'] = name;

    // Add various styles to that style
    type.each(function() {

      // Loop through each style-type and log it's values
      $.each(this.attributes, function() {

        // If it exists... Log it!
        if (this.specified) {
          style[stripPrefix(this.name)] = this.value;
        }

      });

    });;

  });

  console.log('Moment of truth... ' + styles['s57'].FontName);

  $(document).ready(function(){
    $('body').append('<div class="json-output"/>');
    $('.json-output').append(JSON.stringify(styles));
  });

}

Here is what it outputs (JSON- or whatever this is...)

{
  "Default":{
    "Name":"Normal",
    "Vertical":"Bottom",
    "FontName":"Calibri",
    "Family":"Swiss",
    "Size":"11",
    "Color":"#000000"
  },
  "s57":{
    "Name":"Hyperlink",
    "FontName":"Calibri",
    "Family":"Swiss",
    "Size":"11",
    "Color":"#0066CC",
    "Underline":"Single"
  },
  "s58":{
    "Horizontal":"Left",
    "Vertical":"Center",
    "Indent":"1"
  }
  ...
}

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