1

Has anyone deduced syntax which successfully loads XML from file/string and gives access to the data in OS X Yosemite (10.10) Javascript for Automation ?

Documentation and code samples are still fairly thin (as of Nov 2014), and my inductive skills are running dry on three separate approaches to reading an XML (OPML) file at the moment:

  1. Most promising: $.NSXMLDocument

Getting hold of the string data in various ways goes well,

function readTextFromFile(strPath) {
    return $.NSString.stringWithContentsOfFile(strPath);
}
function readDataFromFile(strPath) {
    return $.NSData.alloc.initWithContentsOfFile(strPath);
}
function filePath(strPath) { return Path(strPath); }

But no permutations on this theme have borne fruit:

var strPath='/Users/houthakker/Desktop/notes-2014-11-04.opml',
    dataXML = readData(strPath),
    strXML = readTextFile(strPath),
    oXMLDoc1, oXMLDoc2;

oXMLDoc1 = $.NSXMLDocument.alloc.initWithXMLString(strXML,0,null);
oXMLDoc2 = $.NSXMLDocument.alloc.initWithData(dataXML,0,null);

(the 'function undefined' error messages suggest that those two init functions may not be exposed, though initWithRootElement() does seem to be)

  1. Most progress: $.NSXMLParser

    var oParser = $.NSXMLParser.alloc.initWithData(dataXML);

    return oParser.parse; //true

But event-driven parsing seems to require some further complexities which remain opaque to me, and which may not match my simple needs (reading and converting modestly sized local OPML files).

  1. Most familiar: Application("System Events")

In Applescript this can be done with System Events code:

set fOPML to (POSIX file "/Users/houthakker/Desktop/notes-2014-11-04.opml" as alias) as string
tell application "System Events"
    tell XML file fOPML
    -- now access XML Elements by name or index
end tell

but I haven't found a successful javascript idiom for initializing the XMLFile object with any permutation of a unix Path(), string, or colon-delimited mac path string.

Any thoughts or more successful experience here ?

Ben Zotto
  • 70,108
  • 23
  • 141
  • 204
houthakker
  • 688
  • 5
  • 13

1 Answers1

2

This turns out to work for the (very slow executing) Application("System Events") route:

var app = Application("System Events"),
    strPath = '~/Desktop/summarise.opml';

var oFile = app.xmlFiles[strPath],
    oRoot = oFile.xmlElements[0],
    oHead = oRoot.xmlElements.head,
    oBody = oRoot.xmlElements.body,
    lstOutlineXML = oBody.xmlElements.whose({
        name: 'outline'
    });

And the function for initialising an NSXMLDocument from an XML string is, according to the JSExport convention (in which the letter following each ":" is capitalized, and then the ":"s are removed) .initWithXMLStringOptionsError()

Thus, to choose a local OPML file and parse it to a simple JSON outline:

function run() {
    var app = Application.currentApplication();
    app.includeStandardAdditions = true;

    function readTextFromFile(strPath) {
        return $.NSString.stringWithContentsOfFile(strPath);
    }

    var strPath = (
            app.chooseFile({withPrompt: "Choose OPML file:"})
        ).toString(), // Path → String
        strXML = strPath ? readTextFromFile(strPath) : '';

    if (!strXML) return;

    var oXMLDoc1 = $.NSXMLDocument.alloc.initWithXMLStringOptionsError(strXML, 0, null),
        oRoot = oXMLDoc1.rootElement,
        oBody = ObjC.unwrap(oRoot.children)[1],
        lstOutline =  ObjC.unwrap(oBody.children),
        lstParse, strJSON;

    function parseOPML(lst) {
        var lstParse=[], oNode, dctNode, lstChiln;

        for (var i = 0, lng=lst.length; i<lng; i++) {
            oNode = lst[i];
            dctNode = {};
            dctNode.txt = ObjC.unwrap(oNode.attributeForName('text').stringValue);
            lstChiln = ObjC.unwrap(oNode.children);
            if (lstChiln && lstChiln.length)
                dctNode.chiln = parseOPML(lstChiln);
            lstParse.push(dctNode);
        }
        return lstParse;
    }

    lstParse = parseOPML(lstOutline);
    strJSON = JSON.stringify(lstParse,null, 2);
    app.setTheClipboardTo(strJSON);
    return strJSON;
}
houthakker
  • 688
  • 5
  • 13