git-off

git off handles large files in git repos
git clone https://noulin.net/git/git-off.git
Log | Files | Refs | README

node_parser.js (4387B)


      1 var util = require('../util');
      2 var Shape = require('../model/shape');
      3 
      4 var xml2js = require('xml2js');
      5 
      6 /**
      7  * @api private
      8  */
      9 var options = {  // options passed to xml2js parser
     10   explicitCharkey: false, // undocumented
     11   trim: false,            // trim the leading/trailing whitespace from text nodes
     12   normalize: false,       // trim interior whitespace inside text nodes
     13   explicitRoot: false,    // return the root node in the resulting object?
     14   emptyTag: null,         // the default value for empty nodes
     15   explicitArray: true,    // always put child nodes in an array
     16   ignoreAttrs: false,     // ignore attributes, only create text nodes
     17   mergeAttrs: false,      // merge attributes and child elements
     18   validator: null         // a callable validator
     19 };
     20 
     21 function NodeXmlParser() { }
     22 
     23 NodeXmlParser.prototype.parse = function(xml, shape) {
     24   shape = shape || {};
     25 
     26   var result = null;
     27   var error = null;
     28 
     29   var parser = new xml2js.Parser(options);
     30   parser.parseString(xml, function (e, r) {
     31     error = e;
     32     result = r;
     33   });
     34 
     35   if (result) {
     36     var data = parseXml(result, shape);
     37     if (result.ResponseMetadata) {
     38       data.ResponseMetadata = parseXml(result.ResponseMetadata[0], {});
     39     }
     40     return data;
     41   } else if (error) {
     42     throw util.error(error, {code: 'XMLParserError', retryable: true});
     43   } else { // empty xml document
     44     return parseXml({}, shape);
     45   }
     46 };
     47 
     48 function parseXml(xml, shape) {
     49   switch (shape.type) {
     50     case 'structure': return parseStructure(xml, shape);
     51     case 'map': return parseMap(xml, shape);
     52     case 'list': return parseList(xml, shape);
     53     case undefined: case null: return parseUnknown(xml);
     54     default: return parseScalar(xml, shape);
     55   }
     56 }
     57 
     58 function parseStructure(xml, shape) {
     59   var data = {};
     60   if (xml === null) return data;
     61 
     62   util.each(shape.members, function(memberName, memberShape) {
     63     var xmlName = memberShape.name;
     64     if (Object.prototype.hasOwnProperty.call(xml, xmlName) && Array.isArray(xml[xmlName])) {
     65       var xmlChild = xml[xmlName];
     66       if (!memberShape.flattened) xmlChild = xmlChild[0];
     67 
     68       data[memberName] = parseXml(xmlChild, memberShape);
     69     } else if (memberShape.isXmlAttribute &&
     70                xml.$ && Object.prototype.hasOwnProperty.call(xml.$, xmlName)) {
     71       data[memberName] = parseScalar(xml.$[xmlName], memberShape);
     72     } else if (memberShape.type === 'list') {
     73       data[memberName] = memberShape.defaultValue;
     74     }
     75   });
     76 
     77   return data;
     78 }
     79 
     80 function parseMap(xml, shape) {
     81   var data = {};
     82   if (xml === null) return data;
     83 
     84   var xmlKey = shape.key.name || 'key';
     85   var xmlValue = shape.value.name || 'value';
     86   var iterable = shape.flattened ? xml : xml.entry;
     87 
     88   if (Array.isArray(iterable)) {
     89     util.arrayEach(iterable, function(child) {
     90       data[child[xmlKey][0]] = parseXml(child[xmlValue][0], shape.value);
     91     });
     92   }
     93 
     94   return data;
     95 }
     96 
     97 function parseList(xml, shape) {
     98   var data = [];
     99   var name = shape.member.name || 'member';
    100   if (shape.flattened) {
    101     util.arrayEach(xml, function(xmlChild) {
    102       data.push(parseXml(xmlChild, shape.member));
    103     });
    104   } else if (xml && Array.isArray(xml[name])) {
    105     util.arrayEach(xml[name], function(child) {
    106       data.push(parseXml(child, shape.member));
    107     });
    108   }
    109 
    110   return data;
    111 }
    112 
    113 function parseScalar(text, shape) {
    114   if (text && text.$ && text.$.encoding === 'base64') {
    115     shape = new Shape.create({type: text.$.encoding});
    116   }
    117   if (text && text._) text = text._;
    118 
    119   if (typeof shape.toType === 'function') {
    120     return shape.toType(text);
    121   } else {
    122     return text;
    123   }
    124 }
    125 
    126 function parseUnknown(xml) {
    127   if (xml === undefined || xml === null) return '';
    128   if (typeof xml === 'string') return xml;
    129 
    130   // parse a list
    131   if (Array.isArray(xml)) {
    132     var arr = [];
    133     for (i = 0; i < xml.length; i++) {
    134       arr.push(parseXml(xml[i], {}));
    135     }
    136     return arr;
    137   }
    138 
    139   // empty object
    140   var keys = Object.keys(xml), i;
    141   if (keys.length === 0 || keys === ['$']) {
    142     return {};
    143   }
    144 
    145   // object, parse as structure
    146   var data = {};
    147   for (i = 0; i < keys.length; i++) {
    148     var key = keys[i], value = xml[key];
    149     if (key === '$') continue;
    150     if (value.length > 1) { // this member is a list
    151       data[key] = parseList(value, {member: {}});
    152     } else { // this member is a single item
    153       data[key] = parseXml(value[0], {});
    154     }
    155   }
    156   return data;
    157 }
    158 
    159 module.exports = NodeXmlParser;