asmagill / hammerspoon_asm

Hammerspoon modules in progress...
MIT License
52 stars 12 forks source link

Manipulating an existing XML document using hs._asm.xml? #19

Closed latenitefilms closed 4 years ago

latenitefilms commented 4 years ago

@asmagill - Dumb question... is it possible to load a XML document into hs._asm.xml, manipulate it in Lua-land, then generate a new XML file? Essentially I just want to "edit" an existing XML file.

Here's a test XML.

I'm currently playing around with xml2lua, but not having much luck doing what I want to do.

Any words of wisdom?

asmagill commented 4 years ago

In theory, yes, the NSXMLDocument object can be written as a block of data to a file, and the NSXMLNode objects allow for changing data.

In practice, I usually found it easier to use third party xml libraries, even lua only ones, for most of what I needed to do, though, hence the reason you see this module in its incomplete state.

OTOH, if you're dealing with particularly large files or ones which include large chunks of encoded data (images, or raw binary chunks) I can understand why an Objective-C based solution would be preferable...

I don't know when I'll have a chance to return to this module (other priorities with Hammerspoon atm and finding time for them is sometimes challenging), so you are welcome to take up the challenge of extending or changing it, if you like.

latenitefilms commented 4 years ago

Awesome, thanks for the fast reply @asmagill ! Any 3rd party XML libraries you'd recommend?

latenitefilms commented 4 years ago

@asmagill - Does hs._asm.xml in its current form allow for changing data? Any chance you could give me an example of how to edit an node attribute for example?

asmagill commented 4 years ago

I thought I had used one for a Roku controlling spoon I hope to submit someday, but it looks like I ended up just writing a simple parser to get at what I specifically needed... I'll have to dig through my archives to see if there were others, though the only other one that immediately jumps to mind was a precursor to the objc module where I was trying to follow the same path that Python does for creating its objc bridge by parsing the framework plists... and that's what led me to start the xml module.


In its current form, you'd need to add methods (or modify existing ones) for changing data. Looking at the apple API docs for NSXMLNode, it looks like you can change the name, stringValue, objectValue, and URI properties, though I'm not sure how you'd add/remove children, etc.

You may also need to add a method for dumping the whole thing as a string or data block for writing; I can't recall if there is currently a method for outputting the entire file as an XML string or not...

Resources to look at:

latenitefilms commented 4 years ago

Legend, thanks heaps! I'll leave this issue open for now and have a play.

latenitefilms commented 4 years ago

For anyone playing at home, this is what I used to set a string value.

Thanks heaps @asmagill !

/// hs._asm.xml:setStringValue(value) -> string
/// Method
/// Returns the content of the xmlObject as a string value.
///
/// Parameters:
///  * value - The value you want to set the string value to.
///
/// Returns:
///  * the content of the xmlObject as a string value.
static int xml_setStringValue(lua_State *L) {
    LuaSkin *skin = [LuaSkin sharedWithState:L] ;
    [skin checkArgs:LS_TUSERDATA, USERDATA_TAG, LS_TSTRING, LS_TBREAK] ;

    NSXMLNode *obj = [skin toNSObjectAtIndex:1] ;

    NSString *value = [skin toNSObjectAtIndex:2] ;
    [obj setStringValue:value];

    [skin pushNSObject:[obj stringValue]] ;
    return 1 ;
}
latenitefilms commented 4 years ago

@asmagill - FYI: for some reason I can't work out how to do a pull request for the above (Classic GitHub Desktop doesn't seem to like the sub-modules in this repository), so if you have a chance, feel free to manually merge this code in.

asmagill commented 4 years ago

Can do -- do you have your fork of this repo up somewhere I can see what exact changes you made?

And FYI, I've been moving more and more of these into their own submodules so you only need to pull/edit/fork the ones you actually care about and the code is kept separate... is this one that you'd like me to move to a submodule sooner rather than later?

(The benefit of being a submodule is that you can fork/pull just the ones you want -- you don't have to bother with this "monolithic" list of submodules if you don't want to (or your app has issues with it -- BTW I use SmartGit and my biggest gripe about it and submodules (actually about git in general, as this isn't really a limitation of SmartGit but with how git handles submodules) is that every time I push changes to a submodule, I also have to push the fact that the submodule has changed to this one... so a lot of "updating submodules" entries... but otherwise no complaints)

latenitefilms commented 4 years ago

I literally just added the single function above.

For some reason I was fighting with GitHub, so ended up just copying the code into a branch within CommandPost - see here.

Yes, I think hs._asm.xml could definitely be moved into it's own sub-module. As far as I can tell it's working awesome for my purposes, so one day I'd love to go back and fill in the documentation gaps, and try and get it ready for sharing back into the Hammerspoon core.

asmagill commented 4 years ago

Looking at your specific internal.m file, you have two similar, but slightly different, functions (and the docs for them probably need tweaking as well)

It's been ages since I've perused the NSXML class docs, so what's the difference and are both required?

/// hs._asm.xml:setStringValue(value) -> string
/// Method
/// Returns the content of the xmlObject as a string value.
///
/// Parameters:
///  * value - The value you want to set the string value to.
///
/// Returns:
///  * the content of the xmlObject as a string value.
static int xml_setStringValue(lua_State *L) {
    LuaSkin *skin = [LuaSkin sharedWithState:L] ;
    [skin checkArgs:LS_TUSERDATA, USERDATA_TAG, LS_TSTRING, LS_TBREAK] ;

    NSXMLNode *obj = [skin toNSObjectAtIndex:1] ;

    NSString *value = [skin toNSObjectAtIndex:2] ;
    [obj setStringValue:value];

    [skin pushNSObject:[obj stringValue]] ;
    return 1 ;
}

/// hs._asm.xml:setStringValue(value) -> string
/// Method
/// Returns the content of the xmlObject as a string value.
///
/// Parameters:
///  * value - The value you want to set the string value to.
///
/// Returns:
///  * the content of the xmlObject as a string value.
static int xml_updateStringValue(lua_State *L) {
    LuaSkin *skin = [LuaSkin sharedWithState:L] ;
    [skin checkArgs:LS_TUSERDATA, USERDATA_TAG, LS_TSTRING, LS_TBREAK] ;

    NSXMLElement *obj = [skin toNSObjectAtIndex:1] ;
    NSString *value = [skin toNSObjectAtIndex:2] ;

    //NSXMLElement *element;
    NSXMLNode *node = [[NSXMLNode alloc] initWithKind: NSXMLTextKind];
    [node setStringValue:value];

    [obj addChild: node];

    [skin pushNSObject:[obj stringValue]] ;
    return 1 ;
}
asmagill commented 4 years ago

On another note, have you and your team had a chance to look at hs.text yet? I want to try and get back to it after working on axuielement this weekend. I'd like both to be in core before we decide on a final decision whether "to v2 or not to v2".

latenitefilms commented 4 years ago

Oh, sorry, ignore xml_updateStringValue - I was experimenting with something, but then I later realised that I can use hs._asm.xml:setStringValue(value) to do everything I needed. So ignore xml_updateStringValue.

If you want to see how I'm actually using it in action see here.

We're not currently using hs.text yet. I'll chase up @randomeizer to see if he's had a chance to have a play with it, as to be honest, all the text encoding stuff goes a little bit above my head.

asmagill commented 4 years ago

xml moved to submodule at https://github.com/asmagill/hs._asm.xml and new function applied there. Open new issue there if additional changes are required.