mIRC Home    About    Download    Register    News    Help

Print Thread
JSON, XML, YAML converter #265840 04/08/19 11:16 PM
Joined: Feb 2003
Posts: 2,642
Raccoon Offline OP
Hoopy frood
OP Offline
Hoopy frood
Joined: Feb 2003
Posts: 2,642
Requesting a dll or exe that can convert a file between json, xml, and yaml formats.

Specifically, I will be making hundreds of json and xml urlget requests, and need to convert them to human-readable yaml format.

The JSON, XML and YAML I'll specifically be working with will include tree-level depth, but will probably not contain arrays.
I know I've been getting hung up writing my own parser that supports arrays of trees and trees of arrays. :S

Thanks.


Well. At least I won lunch.
Good philosophy, see good in bad, I like!
Re: JSON, XML, YAML converter [Re: Raccoon] #265841 04/08/19 11:40 PM
Joined: Jul 2006
Posts: 3,559
W
Wims Offline
Hoopy frood
Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 3,559


Looking for a good help channel about mIRC? Check #mircscripting @ irc.swiftirc.net
Re: JSON, XML, YAML converter [Re: Wims] #265858 06/08/19 12:32 AM
Joined: Feb 2003
Posts: 2,642
Raccoon Offline OP
Hoopy frood
OP Offline
Hoopy frood
Joined: Feb 2003
Posts: 2,642
Thanks Ouims. Do you know if those are available in Windows CMD / MS DOS executables?


Well. At least I won lunch.
Good philosophy, see good in bad, I like!
Re: JSON, XML, YAML converter [Re: Raccoon] #265860 06/08/19 02:59 AM
Joined: Apr 2010
Posts: 921
F
FroggieDaFrog Offline
Hoopy frood
Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 921
Do you need it to be call able from mIRC?


Just to make sure I've got this correct: You are making HTTP requests and recieving either JSON or XML data. You then want to convert that data to YAML. Correct?

Are windows COMs a viable option for your need?

Last edited by FroggieDaFrog; 06/08/19 03:07 AM.

I am SReject
My Stuff
Re: JSON, XML, YAML converter [Re: FroggieDaFrog] #265862 06/08/19 08:55 AM
Joined: Feb 2003
Posts: 2,642
Raccoon Offline OP
Hoopy frood
OP Offline
Hoopy frood
Joined: Feb 2003
Posts: 2,642
Yes, using $urlget() to download json or xml data to file, and convert the files to yaml.

If the com calls can be made from mIRC, that would be fine too.


Well. At least I won lunch.
Good philosophy, see good in bad, I like!
Re: JSON, XML, YAML converter [Re: Raccoon] #265864 06/08/19 12:55 PM
Joined: Jul 2006
Posts: 3,559
W
Wims Offline
Hoopy frood
Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 3,559
No. Using a website is not an option?


Looking for a good help channel about mIRC? Check #mircscripting @ irc.swiftirc.net
Re: JSON, XML, YAML converter [Re: Wims] #265865 07/08/19 02:00 AM
Joined: Feb 2003
Posts: 2,642
Raccoon Offline OP
Hoopy frood
OP Offline
Hoopy frood
Joined: Feb 2003
Posts: 2,642
No. I don't think $urlget() works on either of those websites.


Well. At least I won lunch.
Good philosophy, see good in bad, I like!
Re: JSON, XML, YAML converter [Re: Raccoon] #265866 07/08/19 11:15 PM
Joined: Apr 2010
Posts: 921
F
FroggieDaFrog Offline
Hoopy frood
Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 921
I need some info related to the XML and what you want as the resulting YAML from it:

Are there going to be empty nodes that need to be preserved, and if so, how do you want them formatted in the YAML:
Code
<example>
    <child></child>
    <child2 />
</example>


Are there going to be children of the same name, and if so, how do you want them formatted in the YAML:
Code
<example>
    <child>text1</child>
    <child>text2</child>
</example>


Are any of the nodes going to have attributes that need to be preserved, and If so, how do you want those formatted in the YAML:
Code
<example attr="value">
    <child />
</example>



And putting it all together, how would you want something like this formatted in the YAML
Code
<example>
    <child attr="value">text</child>
    <child />
</example>

Last edited by FroggieDaFrog; 07/08/19 11:19 PM.

I am SReject
My Stuff
Re: JSON, XML, YAML converter [Re: FroggieDaFrog] #265867 08/08/19 01:42 AM
Joined: Feb 2003
Posts: 2,642
Raccoon Offline OP
Hoopy frood
OP Offline
Hoopy frood
Joined: Feb 2003
Posts: 2,642
Honestly, I don't have answers for you there. I don't expect any of those edge cases, but I would certainly inform you if they did arise.

I'm not familiar enough with XML to have even seen attribute=value pairs. Would those be treated as regular key=value pairs? And would children of the same name become a list or an array? I just assumed somebody already answered these questions a long long time ago.

The web-based XML to YAML converter that Ouims linked to seems to handle each of your examples. https://codebeautify.org/xml-to-yaml
I do notice that both online tools are not perfectly consistent or reversible, which could probably be chalked up to having bugs of their own.

Last edited by Raccoon; 08/08/19 01:47 AM.

Well. At least I won lunch.
Good philosophy, see good in bad, I like!
Re: JSON, XML, YAML converter [Re: Raccoon] #265877 09/08/19 06:49 AM
Joined: Apr 2010
Posts: 921
F
FroggieDaFrog Offline
Hoopy frood
Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 921
Sorry its taking so long, don't have the hours I used to to pour into things like this. But to show progress (and maybe you know enough about COMs to make use of it) here's the jscript, exposed methods are json2yaml and xml2yaml

Code
(function () {
    
    // Helper Function: returns the subject's cast type
    function getType(subject) {
        return Object.prototype.toString.call(subject).match(/^\[object (\S+)\]$/)[1].toLowerCase();
    }
    
    // Helper Function: returns true if the subject has the specified property and it is not part of its inheritence chain
    function has(subject, key) {
        return Object.prototype.hasOwnProperty.call(subject, key);
    }
    
    // Helper Function: removes EOL characters from text
    function trim(text) {
        return text.replace(/[\r\n]+$/g, '');
    }

    // Helper Function: Escapes text to be YAML compliant
    function yamlText(text) {
        
        // empty text
        if (text === '') {
            return '""';
        }
        
        // text contains characters that require quoting
        if (/(?:^[!&*-?#|>@`"' ])|[{}\[\]\(\),:\t\r\n\\\/]|(?: $)/g.test(text)) {
            return '"' + text.replace(
                /[\\"\u0000-\u001F\u2028\u2029]/g,
                function (chr) {
                    return {'"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t'}[chr] || '\\u' + (chr.charCodeAt(0) + 0x10000).toString(16).substr(1);
                }
            ) + '"';  
        }
        
        // text can be left as-is
        return text;
    }

    // Helper Function: Formats the specified collection item to be YAML compliant
    function yamlItem(data, indent) {
        
        // text
        if (typeof data == 'string') {
            return ' ' + yamltext(data);
        }
        
        // null, boolean, numbers
        if (data === null || data === !!data || isFinite(data)) {
            return ' ' + data;
        }
        
        // collections
        data = yaml(data, indent + 2);
        if (data != undefined) {
            if (data === '[]' || data === '{}') {
                return ' ' + data;
            }
            return '\n' + data;
        }
    }

    // YAML conversion entry point
    function yaml(data, indent) {
        var type = getType(data),
            index,
            value,
            hasChild,
            result = '';

        // no data, return
        if (data === undefined) {
            return;
        }
        
        // text
        if (type == 'string') {
            return yamlText(data);
        }
        
        // null, boolean, numbers
        if (data == null || type == 'boolean' || isFinite(data)) {
            return '' + data;
        }
        
        // collections
        if (type == 'array' || type == 'object') {
            
            // loop over each item in collection
            for (index in data) {
                if (has(data, index)) {
                    
                    // convert to YAML item and append to result
                    value = yamlItem(data[index], indent);
                    if (value != null) {
                        hasChild = true;
                        result += Array(indent + 1).join(' ') + (type == 'array' ? '-' : yamlText(index) + ':') + trim(value) + '\n';
                    }
                }
            }
            
            // return result
            return trim(hasChild ? result : type == 'array' ? '[]' : '{}') + '\n';
        }
    }

    // Helper Function: Recursively walks an XML document, processing it into a proper JS collection
    function xmlWalk(node, parent) {
        var result = {
                attributes: {},
                children: {},
                value: node.text
            },
            index,
            attr,
            child;

        // loop over all attributes and add them to the result's attributes collection
        for (index = 0; index < node.attributes.length; index += 1) {
            attr = node.attributes[index];
            result.attributes[attr.nodeName] = attr.text || true;
        }
        
        // loop over all children and add them to the result's attributes collection
        for (index = 0; index < node.childNodes.length; index += 1) {
            child = node.childNodes[index];
            if (child.nodeType === 1) {
                xmlWalk(child, result.children);
            }
        }
        
        // if theres no attributes and no children nodes, store this node's value as being its text
        if (node.childNodes.length === 0 && node.attributes.length === 0) {
            result = node.text || true;
        }

        // store the node based on the parent:
        // Parent does not have entry for the node name: add entry with result as its value
        if (!has(parent, node.nodeName)) {
            parent[node.nodeName] = result;

        // parent's entry for node name is an array: append result
        } else if (getType(parent[node.nodeName]) == 'array') {
            parent[node.nodeName].push(result);

        // parent's entry for node name is not an array: convert to array and append result
        } else {
            parent[node.nodeName] = [parent[node.nodeName], result];
        }
    }
    
    // Exposed Function: Converts an XML string into YAML
    xml2yaml = function (data) {
        
        // initial XML parser
        var xml = new ActiveXObject("Msxml2.DOMDocument.6.0");
        xml.async = false;
        
        // load xml into parser
        try {
            var loaded = xml.loadXML(data);
        } catch (e) {
            throw new Error('INVALID_XML');
        }
        if (!loaded || xml.documentElement == null) {
            throw new Error('INVALID_XML');
        }
        
        // walk the XML, converting it to a more managable JS object
        xmlWalk(xml.documentElement, data = {});
        
        // convert the JS object to YAML
        return yaml(data, 0);
    };
    
    // Exposed Function: Converts a JSON string into a YAML
    json2yaml = function (data) {
        
        // parse JSON into JS object
        data = String(data).replace(/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, function(chr) {
            return '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
        });
        if (!/^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
            throw new Error("INVALID_JSON");
        }
        try {
            data = eval('(' + data + ')');
        } catch (e) {
            throw new Error("INVALID_JSON");
        }
        
        // convert JS object to YAML
        return yaml(data, 0);
    };
}());

Last edited by FroggieDaFrog; 09/08/19 10:06 AM.

I am SReject
My Stuff
Re: JSON, XML, YAML converter [Re: Raccoon] #265879 09/08/19 10:15 AM
Joined: Apr 2010
Posts: 921
F
FroggieDaFrog Offline
Hoopy frood
Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 921
as far as inconsistencies: JSON to YAML is a 1:1 conversion as YAML supports all data structures of JSON(infact a JSON string is considered valid YAML). With XML that is not the case and there is no official guidelines as to perform the conversion so many converters roll their own formatting.

So take your pick as to which you want assuming the input data is as follows:
Code
<nodes>
    <node>text</node>
    <node2 some_attr="attr_value">
        text2
        <node3 />
    </node2>
</nodes>



Always include attributes and children
Recommended due to being a consistent format, there is no 'extra processing' in determining if the entry's value is what was the text or if its a collection containing attributes, children and text
Code
nodes:
  attributes: {}
  children:
    node:
      attributes: {}
      children: {}
      value: text
    node2:
      attributes:
        some_attr: attr_value
      children:
        node3:
          attributes: {}
          children: {}
          value: ""
      value: text2
  value: ""


Only include attributes and children when such exist, if neither exist, default to text. if text is missing, default to true
Code
nodes:
  children:
    node: text
    node2:
      attributes:
        some_attr: attr_value
      children: 
        node3: true
      value: text2




Last edited by FroggieDaFrog; 09/08/19 10:31 AM.

I am SReject
My Stuff
Re: JSON, XML, YAML converter [Re: FroggieDaFrog] #265897 10/08/19 11:07 PM
Joined: Feb 2003
Posts: 2,642
Raccoon Offline OP
Hoopy frood
OP Offline
Hoopy frood
Joined: Feb 2003
Posts: 2,642
Interesting. I would disagree though about the 'always include imaginary attributes and imaginary children' as being "recommended" given how that quickly pollutes the collection and now converting to a JSON or YAML generates a huge ugly balloon of nonsense 100% of the time, which neither of the other format conversions would do until XML is brought into the mix. I wouldn't want to see one thousand empty _attr lines in my .yaml files just because the data arrived via XML.

Last edited by Raccoon; 10/08/19 11:07 PM.

Well. At least I won lunch.
Good philosophy, see good in bad, I like!
Re: JSON, XML, YAML converter [Re: Raccoon] #265899 11/08/19 06:30 AM
Joined: Apr 2010
Posts: 921
F
FroggieDaFrog Offline
Hoopy frood
Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 921
mkay, so short of bugs, I think I've finished the jscript. I should be able to implement within mIRC in the next day or two

Code
(function (trim, esc) {
    var xml = new ActiveXObject("Msxml2.DOMDocument.6.0");
    xml.async = false;

    // Helper Function: returns the subjects cast type
    function getType(subject) {
        return Object.prototype.toString.call(subject).match(/^\[object (\S+)\]$/)[1].toLowerCase();
    }

    // Helper Function: formats text to be valid YAML
    function yamlTextEscape(text) {

        // Empty string
        if (text === '') {
            return '""';
        }

        // String needs to be quoted:
        //   Starts with: ! & * - ? # | > @ ` " '
        //   Contains: { } [ ] ( ) , : \ / tab carriage-return line-feed
        //   Ends with space
        //   is: void null true false
        if (/(?:^[!&*-?#|>@`"' ])|[{}\[\]\(\),:\\\/\t\r\n]|(?: $)|(?:^(?:void|null|true|false)$)/g.test(text)) {
            return '"' + text.replace(
                /[\\"\u0000-\u001F\u2028\u2029]/g,
                function (chr) {
                    return {'"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t'}[chr] || '\\u' + (chr.charCodeAt(0) + 0x10000).toString(16).substr(1);
                }
            ) + '"';
        }

        // String can be left as-is
        return text;
    }

    // Formats input into valid YAML
    yaml = function (data, indent) {

        // undefined: do nothing
        if (data === undefined) {
            return;
        }

        var type = getType(data),
            key,
            value,
            hasChild,
            prefix,
            result = [];

        // Null, Boolean, Number: convert to string
        if (data === null || data === !!data || (type === 'number' && isFinite(data))) {
            return '' + data;
        }

        // String: escape text
        if (type === 'string') {
            return yamlTextEscape(data);
        }

        // Array
        if (type === 'array') {

            // Generate prefix indent
            prefix = Array(indent + 1).join(' ') + '-';

            // Loop over each item in array
            for (key = 0; key < data.length; key += 1) {

                // Invalid
                if (data[key] === undefined) {
                    continue;
                }

                // Get item and item type
                value = data[key];
                type = getType(value);

                // String
                if (type === 'string') {
                    result.push(prefix + ' ' + yamlTextEscape(value));

                // Null, Boolean, Number: convert to string
                } else if (value === null || value === !!value || (type === 'number' && isFinite(value))) {
                    result.push(prefix + ' ' + value);

                // Array
                } else if (type === 'array') {
                    value = yaml(value, indent + 2);

                    // value is empty array
                    if (value === '[]') {
                        result.push(prefix + ' []');

                    // Value is populated collection
                    } else {
                        result.push(prefix, value);
                    }

                // Collection
                } else if (type === 'object') {
                    value = yaml(value, indent + 2);

                    // value is empty collection
                    if (value === '{}') {
                        result.push(prefix + ' {}');

                    // Value is populated collection
                    } else {
                        result.push(prefix, value);
                    }

                // Unkown type
                } else {
                    continue;
                }

                // Subject array has atleast one valid child
                hasChild = true;
            }

            if (hasChild) {
                return result.join('\n');
            }

            // no valid items in the array
            return '[]';
        }

        // Collection
        if (type === 'object') {

            // Loop over each item in the collection
            for (key in data) {

                // Invalid item
                if (!Object.prototype.hasOwnProperty.call(data, key) || data[key] === undefined) {
                    continue;
                }

                // Get item and item type
                value = data[key];
                type = getType(value);

                // Prepare indent prefix
                prefix = Array(indent + 1).join(' ') + yamlTextEscape(key) + ':';

                // String
                if (type === 'string') {
                    result.push(prefix + ' ' + yamlTextEscape(value));

                // Null, Boolean, Number: convert to string
                } else if (value === null || value === !!value || (type === 'number' && isFinite(value))) {
                    result.push(prefix + ' ' + value);

                // Array
                } else if (type === 'array') {
                    value = yaml(value, indent + 2);

                    // value is empty array
                    if (value === '[]') {
                        result.push(prefix + ' []');

                    // Value is populated collection
                    } else {
                        result.push(prefix, value);
                    }

                // Collection
                } else if (type === 'object') {
                    value = yaml(value, indent + 2);

                    // value is empty collection
                    if (value === '{}') {
                        result.push(prefix + ' {}');

                    // Value is populated collection
                    } else {
                        result.push(prefix, value);
                    }

                // Unkown type
                } else {
                    continue;
                }

                // Subject collection has atleast one valid child
                hasChild = true;
            }

            // Collection has valid items
            if (hasChild) {
                return result.join('\n');
            }

            // empty collection
            return '{}';
        }
    }

    function xmlWalk(node, parent) {

        var text = (node.text === undefined || node.text === null || node.text === '') ? true : node.text,
            result,
            index,
            attr,
            attributes = {};

        // No attributes or children
        if (node.attributes.length === 0 && node.childNodes.length === 0) {
            result = text;

        } else {

            // prep result object
            result = {
                '@value': text
            };

            // Add attributes to result object
            for (
                index = 0;
                index < node.attributes.length;
                index += 1
            ) {
                attr = node.attributes[index];
                result['@@' + attr.nodeName] = attr.text === undefined ? true : attr.text;
            }

            // Add children to result object
            for (
                index = 0;
                index < node.childNodes.length;
                index += 1
            ) {
                child = node.childNodes[index];
                if (child.nodeType === 1) {
                    xmlWalk(child, result);
                }
            }
        }

        // Parent has entry for node
        if (Object.prototype.hasOwnProperty.call(parent, node.nodeName)) {

            // Entry for node is array
            if (getType(parent[node.nodeName]) === 'array') {
                parent[node.nodeName].push(result);

            // Entry for node is not an array
            } else {
                parent[node.nodeName] = [parent[node.nodeName], result];
            }

        // Parent does not have entry for node
        } else {
            parent[node.nodeName] = result;
        }

        return parent;
    }

    // Exposed Function: converts xml into valid yaml
    xml2yaml = function (data) {
        try {
            var loaded = xml.loadXML(data);
        } catch (e) {
            throw new Error('INVALID_XML');
        }
        if (!loaded || xml.documentElement == null) {
            throw new Error('INVALID_XML');
        }
        return yaml(xmlWalk(xml.documentElement, {}), 0);
    };

    // Exposed Function: converts json into valid yaml
    json2yaml = function (data) {
        data = String(data).replace(/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, function(chr) {
            return '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
        });
        if (!/^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
            throw new Error("INVALID_JSON");
        }
        try {
            data = eval('(' + data + ')');
        } catch (e) {
            throw new Error("INVALID_JSON");
        }
        return yaml(data, 0);
    };
}());


/* Uncomment for testing - run with cscript file.js from cmd.exe
WScript.echo(json2yaml('{"key": "value"}'));

WScript.echo(xml2yaml('<node />'));
*/



The format I've gone with:
Code
<root>
    <child1 />
    <child2>text</child2>
    <child3 attr="attr_value" />
    <child4 attr="attr_value">text</child4>
</root>

becomes

root
  child1: true
  child2: text
  child3:
    "@@attr": attr_value
    "@value": true
  child4:
    "@@attr": attr_value
    "@value": text

Last edited by FroggieDaFrog; 11/08/19 08:11 AM.

I am SReject
My Stuff
Re: JSON, XML, YAML converter [Re: FroggieDaFrog] #265900 11/08/19 07:30 AM
Joined: Feb 2003
Posts: 2,642
Raccoon Offline OP
Hoopy frood
OP Offline
Hoopy frood
Joined: Feb 2003
Posts: 2,642
Is this the future, where complex mirc scripts get written in jscript? smile Thanks man.

I wonder if Khaled would pick this up and just add support for collections and these formats. Hash tables two point oh.


Well. At least I won lunch.
Good philosophy, see good in bad, I like!
Re: JSON, XML, YAML converter [Re: Raccoon] #265901 11/08/19 08:12 AM
Joined: Apr 2010
Posts: 921
F
FroggieDaFrog Offline
Hoopy frood
Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 921
Its just easier working with an OOP language


I am SReject
My Stuff
Re: JSON, XML, YAML converter [Re: Raccoon] #265902 11/08/19 10:17 AM
Joined: Apr 2010
Posts: 921
F
FroggieDaFrog Offline
Hoopy frood
Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 921
$JSONtoYAML(&in[, &out])
Converts JSON to YAML
&in Binary variable containing JSON data
&out Binary variable to store the output YAML in - If not specified, the result overwrites the contents of &in
Returns $true if sucessful
Returns $false <reason> if failure

$XMLtoYAML(&in[, &out])
Converts XML to YAML
&in Binary variable containing XML data
&out Binary variable to store the output YAML in - If not specified, the result overwrites the contents of &in
Returns $true if sucessful
Returns $false <reason> if failure

Code
alias JSONtoYAML {
  if ($0 < 2) {
    return $toYAML($1)
  }
  return $toYAML($1, $2)
}
alias XMLtoYAML {
  if ($0 < 2) {
    return $toYAML($1).xml
  }
  return $toYAML($1, $2).xml
}

;; jscript that does the heavy lifting
alias -l js {
  return (function(A,B,C){C.async=!1;function D(a,b){if(a===undefined)return;var c=A(a),d,e,f,g,h=[];if(a===null||a===!!a||(c=='number'&&isFinite(a)))return''+a;if(c=='string')return B(a);if(c=='array'){g=Array(b+1).join(' ')+'-';for(d=0;d<a.length;d+=1){if(a[d]===undefined)continue;c=A(e=a[d]);if(c=='string')h.push(g+' '+B(e));else if(e=== null||e===!!e||(c=='number'&&isFinite(e)))h.push(g+' '+e);else if(c=='array'){e=D(e,b+2);if(e=='[]')h.push(g+' []');else h.push(g,e)}else if(c=='object'){e=D(e,b+2);if(e=='{}')h.push(g+' {}');else h.push(g,e)}else continue;f=!0}return f?h.join('\n'):'[]'}if(c=='object'){for(d in a){if(!Object.prototype.hasOwnProperty.call(a,d)||a[d]===undefined)continue;e=a[d];c=A(e);g=Array(b+1).join(' ')+B(d)+':';if(c=='string')h.push(g+' '+B(e));else if(e===null||e===!!e||(c=='number'&&isFinite(e)))h.push(g+' '+e);else if(c=='array'){e=D(e,b+2);if(e=='[]')h.push(g+' []');else h.push(g,e)}else if(c=='object'){e=D(e,b+2);if(e=='{}')h.push(g+' {}');else h.push(g,e)}else continue;f=!0}return f?h.join('\n'):'{}'}}function E(a,b){var c=(a.text===undefined||a.text===null||a.text==='')?!0:a.text,d={'@value':c},e=0,f;if(a.attributes.length==0&&a.childNodes.length==0)d=c;else{for(;e<a.attributes.length;e++){f=a.attributes[e];d['@@'+f.nodeName]=f.text===undefined?!0:f.text}for(e=0;e<a.childNodes.length;e++){f=a.childNodes[e];if(f.nodeType==1)E(f,d)}}if(Object.prototype.hasOwnProperty.call(b,a.nodeName)){if(A(b[a.nodeName])=='array')b[a.nodeName].push(d);else b[a.nodeName]=[b[a.nodeName],d]}else b[a.nodeName]=d;return b}xml2yaml=function(a,b){try{b=C.loadXML(a)}catch(e){throw new Error('INVALID_XML')}if(!b||C.documentElement==null)throw new Error('INVALID_XML');return D(E(C.documentElement,{}),0)};json2yaml=function(a){a=a.replace(/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,function(a){return'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4)});if(!/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,'')))throw new Error("INVALID_JSON");try{a=eval('('+a+')')}catch(e){throw new Error("INVALID_JSON")}return D(a,0)}}(function(a){return Object.prototype.toString.call(a).match(/^\[object (\S+)\]$/)[1].toLowerCase()},function(a){if(a==='')return'""';if(/(?:^[!&*-?#|>@`"' ])|[{}\[\]\(\),:\\\/\t\r\n]|(?:\x20$)|(?:^(?:void|null|true|false)$)/g.test(a)){return'"'+a.replace(/[\\"\u0000-\u001F\u2028\u2029]/g,function(a){return{'"':'\\"','\\':'\\\\','\b':'\\b','\f':'\\f','\n':'\\n','\r':'\\r','\t':'\\t'}[a]||'\\u'+(a.charCodeAt(0)+0x10000).toString(16).substr(1)})+'"';}return a},new ActiveXObject('Msxml2.DOMDocument.6.0')))
}

;; cleanup because we ain't filthy hobbits
alias -l cleanup {
  if ($com(SReject/toYAML/JSEngine)) {
    .comclose SReject/toYAML/JSEngine
  }
  if ($com(SReject/toYAML/JSShell)) {
    .comclose SReject/toYAML/JSShell
  }
  if ($timer(SReject/toYAML/cleanup)) {
    .timerSReject/toYAML/cleanup off
  }
}

;; does the heavy lifting
alias toYAML {


  if (!$isid) {
    if ($1 == cleanup || $1 == -c) {
      cleanup
    }
  }

  var %call = $iif($prop === xml, xml2yaml, json2yaml)
  var %out = $iif($0 < 2, $1, $2)

  ;; prep COM
  if (!$com(SReject/toYAML/JSShell) || !$com(SReject/toYAML/JSEngine)) {
    cleanup
    .comopen SReject/toYAML/JSShell MSScriptControl.ScriptControl
    if (!$com(SReject/toYAML/JSShell) || $comerr) {
      cleanup
      return $false Failed to create script engine
    }
    elseif (!$com(SReject/toYAML/JSShell, language, 4, bstr, jscript) || $comerr) {
      cleanup
      return $false Failed to set script engine language
    }
    elseif (!$com(SReject/toYAML/JSShell, AllowUI, 4, bool, $false) || $comerr) {
      cleanup
      return $false Failed to disable script engine UI
    }
    elseif (!$com(SReject/toYAML/JSShell, timeout, 4, integer, -1) || $comerr) {
      cleanup
      return $false Failed to set script engine time out
    }
    elseif (!$com(SReject/toYAML/JSShell, ExecuteStatement, 1, bstr, $js) || $comerr) {
      cleanup
      return $false Failed to initialize jscript
    }
    elseif (!$com(SReject/toYAML/JSShell, Eval, 1, bstr, this, dispatch* SReject/toYAML/JSEngine) || $comerr || !$com(SReject/toYAML/JSEngine)) {
      cleanup
      return $false Failed to retrieve js engine reference
    }
  }

  ;; cleanup in 5 seconds - done to allow for batching
  .timerSReject/toYAML/Cleanup -io 1 5 cleanup

  ;; call appropriate conversion function
  if (!$com(SReject/toYAML/JSEngine, %call, 1, &bstr, $1) || $comerr) {
    return $false Conversion failed
  }

  ;; clear output bvar  
  if ($bvar(%out)) {
    bunset %out
  }

  ;; fill output bvar with yaml result
  noop $com(SReject/toYAML/JSEngine, %out).result

  return $true
}

Last edited by FroggieDaFrog; 11/08/19 10:28 AM.

I am SReject
My Stuff