0

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!

P.S. 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"
}
]
...
11
  • 3
    That's not JSON, it's just a regular Javascript object. Commented Mar 4, 2015 at 22:30
  • Have you thrown a debugger statement in before assigning the value to see what the value is? Commented Mar 4, 2015 at 22:34
  • @Nit, so I have been told. I see '{' and '[', and I mistake it for JSON. Could you explain what would make this "JSON"? The only thing I changed from json.org/example was the var styles = []' (Swapped the {''s for a '['. I see '[ & ]' used in "JSON" tutorials. How would this be reworked to be "JSON"? Commented Mar 4, 2015 at 23:12
  • @Daved Where in the script should I try that? (I have used a bunch of console.log() lines all over the file. Everything is fine up until style[this.name] = this.value. I got that little gem from another post similar to what I need. I just put it in the $.each() loop and integrated my own stuff (that reads the element's attributes). Commented Mar 4, 2015 at 23:15
  • 1
    @Daved what he has above is a JavaScript array literal. JSON is a string (or if the contents of the file started at the opening bracket and ended at the closing bracket, which would effectively be a string in usage.) The var styles = is what makes it a literal and not JSON. Commented Mar 4, 2015 at 23:57

3 Answers 3

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)

Sign up to request clarification or add additional context in comments.

2 Comments

Nice. I will play around with this too... It looks completely different from what I cam up with finally... I am updating my answer as we speak :) Thank you for this. I think your script is more optimized than mine...
I marked this as the answer, because it provides exactly what I asked for originally. You sir, are a rock star! This works great! Thanks :)
0

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"
  }
  ...
}

2 Comments

I decided against the '[' which like everyone says- makes the whole thing an object. I also removed a tier from the output as well. Not as a compromise, but because it wasn't necessary for what I wanted (which was the ability to reference these values with styles['s57'].FontName and the like :)
I guess I should have also elaborated on the test I did. I was using jQuery.get() to load the XML. I will update my answer with the full code so you can see what I was doing. Sorry about that.
0

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.
    });
});

4 Comments

Sweet, gonna give this a shot! Will post back... Thanks!
This outputs the same thing as my original script... I guess I am unsure how to insert values into different "tiers" of the object. The this.name and this.value are outputting the correct values. Oddly, the JSON.stringify(styles) is outputting '[]', not what I was expecting (which was a JSON-looking string with '{' and '['.
I updated my OP with some changes that seem to have gotten me a step closer... Maybe it can shed some light on what I can do to get the desired outcome? Thanks for your help btw...
NP. Read your other answer and comments. Realized I left a crucial part out for you. I edited my post so you could see what I did. jQuery.get() is a shorthand way of loading like your jQuery.ajax example.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.