0

I'm trying to build an xml file from 2 JSON objects through 2 cascading loops. In the second loop, I can't find how to pass a value from the previous loop:

In the example below, I would like to pass the "docnumber" value to my second loop (through GroupsACLJSON) in order to build the "acl" node from the second JSON object, but using "this.docnumber" from the parent object property returns "undefined", thus leading to an empty "acl" node.

var documentsJSON = [{
    "folder": "Enterprise1",
    "extension": "DOCX",
    "docnumber": "3912271",
    "version": "1"
  },
  {
    "folder": "Enterprise2",
    "extension": "MSG",
    "docnumber": "3912298",
    "version": "1"
  },
  {
    "folder": "Enterprise3",
    "extension": "DOCX",
    "docnumber": "3912692",
    "version": "1"
  }
]

var GroupsACLJSON = [{
    "docNumber": "3912271",
    "groupName": "group1"
  },
  {
    "docNumber": "3912271",
    "groupName": "group2"
  },
  {
    "docNumber": "3912298",
    "groupName": "group3"
  },
  {
    "docNumber": "3912298",
    "groupName": "group4"
  }
]

// importing and declaring xmlbuilder variable: 
var builder = require('xmlbuilder');
var xmlObjectImporter = builder.create('import', {
  version: '1.0',
  encoding: 'UTF-8',
  standalone: true
}, {
  headless: false,
  stringify: {}
});

var nodeArray = [];
var xmlObjectElement = {
  node: function() {
    for (var i = 0; i < documentsJSON.length; i++) {
      // populate the nodeObject for each row in documentsJSON and add it to the nodeArray:
      var nodeObject = {
        location: documentsJSON[i].folder,
        category: {
          attribute: [{
            '#text': documentsJSON[i].docnumber,
            '@name': "Document Number"
          }],
          '@name': "ACME",
        },
        docnumber: documentsJSON[i].docnumber,
        // loop through GroupsACLJSON to find if we have specific ACL groups for this document:
        acl: function() {
          var documentNumber = this.docnumber
          console.log(this.docnumber);
          var acl = [];
          var aclObject = {};
          for (var j = 0; j < GroupsACLJSON.length; j++) {
            if (GroupsACLJSON[j].docNumber == documentNumber) {
              aclObject = {
                '@group': GroupsACLJSON[j].groupName,
                '@permissions': '111111100'
              };
              acl.push(aclObject);
            };
          };
          return acl;
        },
        '@type': "document",
        '@action': "create",
      };
      nodeArray.push(nodeObject);
    };
    return nodeArray;
  }
};

// writing our elements in the xml file using the XML object:
var ele = xmlObjectImporter.ele(xmlObjectElement);

console.log(xmlObjectImporter.toString({
  pretty: true
}));

Here is an output I would expect from this:

<import>
  <node type="document" action="create">
    <location>Enterprise1</location>
    <category name="ACME">
      <attribute name="Document Number">3912271</attribute>
    </category>
    <docnumber>3912271</docnumber>
    <acl group="group1" permissions="111111100" />
    <acl group="group2" permissions="111111100" />
  </node>
  <node type="document" action="create">
    <location>Enterprise2</location>
    <category name="ACME">
      <attribute name="Document Number">3912298</attribute>
    </category>
    <docnumber>3912298</docnumber>
    <acl group="group3" permissions="111111100" />
    <acl group="group4" permissions="111111100" />
  </node>
  <node type="document" action="create">
    <location>Enterprise3</location>
    <category name="ACME">
      <attribute name="Document Number">3912692</attribute>
    </category>
    <docnumber>3912692</docnumber>
    <acl/>
  </node>
</import>
user7637745
  • 965
  • 2
  • 14
  • 27
harzack86
  • 1
  • 4
  • Is there a particular reason you are defining all of your functions as methods in the xmlObjectElement? I honestly have an extremely hard time following what is going on here (if I am alone on this please tell me). My first though would be to make a function which accepts both of your arrays as arguments, and returns your xmlObjectElement. Break out the logic into separate functions. If you have an example of your desired output (xmlObjectElement) I could help more. – nxSolari Jul 05 '18 at 15:37
  • Thank you, I'll look into that. I'm rather new to node.js and programming, so this might be why I'm over complicating it :) – harzack86 Jul 05 '18 at 16:27

2 Answers2

0

You have several undeclared variables that will cause your code to break when running in strict mode.

  1. i and j in your for loops both need declared. e.g. let i = 0.
  2. The aclObject is not declared. You need to add a var declaration, e.g. const aclObject = { ... }.

After fixing those, I had no trouble running your code, at least, I had no trouble building the xmlObjectElement and executing the node() and acl() functions, which worked.

UPDATED

This will not fix the way that xml-builder is traversing your object and building the xml, but your functions could be improved by using Array methods instead of for loops. You'd have no need to use i or j and decrease the chances of introducing a bug for that reason.

For instance, you could easily replace both loops with a use of .map(). See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map.

var xmlObjectElement = {
  node: function () {
    return documentsJSON.map((document) => {
      // populate the nodeObject for each row in documentsJSON and add it to the nodeArray:
      return {
        location: document.folder,
        category: {
          attribute: [
            { '#text': document.docnumber, '@name': "Document Number" }
          ],
          '@name': "ACME",
        },
....
})
Chad Moore
  • 734
  • 4
  • 15
  • After you made those changes are you getting populated `` elements in the resulting xml? – Mark Jul 05 '18 at 16:10
  • I've updated the code, made the example simpler and added a desired output. The elements are not populated as desired though. – harzack86 Jul 05 '18 at 16:42
  • Yes. I digged a bit further, and it seems odd how javaScript traverses my code: its like the second loop for the acl method is ran only after the first loop is completed, so the value of the variable "i" when displayed in the acl method is always "3", i.e. the last value of the node method loop... – harzack86 Jul 05 '18 at 18:29
  • @harzack86 added some thoughts on improving your loops. This should still be helpful even with your solution below. Oh, and upvotes are a nice way to thank folks who've helped you. – Chad Moore Jul 06 '18 at 14:54
  • @ChadMoore Thanks for the additional comment. The .map() method is definitely something for me to explore and improve my code. You have my upvote, although not displayed as I don't have any "reputation" here yet :) – harzack86 Jul 12 '18 at 15:03
0

Following nxSolari's suggestion, I re-wrote the code using 2 functions instead of a big object and methods and got the desired result. I still don't understand how the code gets through the objects and methods, but here is the new working code:

    'use strict';

    var documentsJSON = [{
        "folder":"Enterprise1" , 
        "extension":"DOCX" , 
        "docnumber":"3912271" , 
        "version":"1"
        },
        {
        "folder":"Enterprise2" , 
        "extension":"MSG" , 
        "docnumber":"3912298" , 
        "version":"1"
        },
        {
        "folder":"Enterprise3" , 
        "extension":"DOCX" , 
        "docnumber":"3912692" , 
        "version":"1"
        }
    ]

    var GroupsACLJSON = [{
        "docNumber":"3912271" , 
        "groupName":"group1"
        },
        {
        "docNumber":"3912271" , 
        "groupName":"group2"
        },
        {
        "docNumber":"3912298" , 
        "groupName":"group3"
        },
        {
        "docNumber":"3912298" , 
        "groupName":"group4"
        },
        {
        "docNumber":"3912692" , 
        "groupName":"group5"
        }
    ]


    // importing and declaring xmlbuilder variable: 
    var builder = require('xmlbuilder');
    var xmlObjectImporter = builder.create('import', {
        version: '1.0', 
        encoding: 'UTF-8', 
        standalone: true
    }, {
        headless: false, 
        stringify: {}
    });

    var xmlObjectElement = {};

    function buildACLnode(passedDoc, acls) {
        var acl= [];
        for (var jDoc = 0 ; jDoc < acls.length ; jDoc++) {
            if (acls[jDoc].docNumber == passedDoc) {
                var aclObject = {
                    '@group':  acls[jDoc].groupName,
                    '@permissions': '111111100'
                };
                acl.push(aclObject);
            };
        };
        return acl;
    }

    function buildXML(documents) {
        var nodeArray = [];
        for (var iDoc = 0; iDoc < documents.length; iDoc++) {
            var nodeObject = {
                    node: {
                        location: documentsJSON[iDoc].folder,
                        category: {
                            attribute: [
                                { '#text': documentsJSON[iDoc].docnumber, '@name': "Document Number" }
                                ],
                            '@name': "ACME",
                        },
                        acl: buildACLnode(documentsJSON[iDoc].docnumber, GroupsACLJSON),
                        '@type': "document",
                        '@action': "create",
                        }
                };
            nodeArray.push(nodeObject);
        };
        return nodeArray;
    }

    xmlObjectElement = buildXML(documentsJSON);

    // writing our elements in the xml file using the XML object:
    var ele = xmlObjectImporter.ele(xmlObjectElement);

    console.log(xmlObjectImporter.toString({ 
        pretty: true 
    }));
harzack86
  • 1
  • 4