AppleScript: Set variable to every XML element of a given name

Discussion in 'Mac Programming' started by moonman239, Dec 18, 2012.

  1. macrumors 65816

    Joined:
    Mar 27, 2009
    #1
    Dear fellow AppleScript programmers,

    I have an iOS app that has recipes. Each recipe can be organized by course or by cuisine. Each course and cuisine have their own table view and controller. I need to make sure each recipe is linked to a course and cuisine. Since a storyboard is really an XML file containing all the UI elements, I figured I could create an AppleScript that searches all the table view cells to make sure that all recipes are linked to two table view cells.

    Here is my code:

    Code:
    
    set the_file to ((choose file without invisibles) as string)
    tell application "System Events"
    	set xml_data to contents of XML file the_file
    	tell xml_data to set tableViewCells to every XML element of xml_data whose name is "tableViewCell"
    	tell xml_data to set subViews to every XML element of tableViewCells whose name is "subviews"
    	tell xml_data to set viewLinks to every XML element of subViews whose name is "view"
    	tell xml_data to set viewControllers to every XML element whose name is "viewController"
    	tell xml_data to set views to every XML element of viewControllers whose name is "view"
    	tell xml_data to set navItems to first XML element of views whose name is "navigationItem"
    	tell xml_data to set recipes to value of XML attribute "title" of XML element navItems
    end tell
    
    When I try to run this code, I get the error "Can't get every element of {} whose name = "subviews"". I just don't get why the fourth line of code results in tableViewCells being set to nothing useful. The tag names are correct.

    I could do the job myself, but I just don't want to.
     
  2. macrumors 603

    Joined:
    Aug 9, 2009
    #2
    What does this error message tell you, or at least what does it suggest to you?
    Code:
    "Can't get every element of {} whose name = "subviews"".
    What it suggests to me is you have an empty array (shown as {} in the error message), so asking for "every element" of an empty array is an impossible request to fulfill.

    So work backwards from that, possibly using a debugger.

    At the very least, you will need to apply the debugging technique of Break It Down. In this case, that means running the script with as few commands as possible, then looking carefully at each result. For example, the starting point is probably something like:
    Code:
    set the_file to ((choose file without invisibles) as string)
    tell application "System Events"
      set xml_data to contents of XML file the_file
      return xml_data
    end tell
    
    If you have an AppleScript debugging tool, you might have other techniques you can apply. Otherwise you're going to have to do the equivalent of single-stepping, which means you need to start from the smallest thing that works (e.g. the above example), then gradually add things until it breaks.

    Another possibility might be to use some other scripting language that can parse XML, yet gives you better debugging capability.


    Not sure what you mean by not wanting to do the job yourself. If you're referring to not wanting to find the problem by yourself, then what would someone else need in order to start finding the problem for you?
     
  3. ytk
    macrumors regular

    Joined:
    Jul 8, 2010
    #3
    Don't use AppleScript for this. The only real use for AppleScript is communicating with applications that support AppleScript. If you have experience with any other programming language that can do XML easily (e.g., Ruby, Python, Perl) you'll find it much simpler, faster, and more robust to write it in that language. My guess is that you'd be able to install any needed XML modules for your chosen language, figure out how to use them, and write, test, and fully debug your program before you got any sort of XML parsing working flawlessly in AppleScript.

    Heck, even if you have to learn the entire language starting with "Hello World", you might still be able to do it more quickly than with AppleScript. :rolleyes:
     
  4. thread starter macrumors 65816

    Joined:
    Mar 27, 2009
    #4
    OK, for some reason, "set tableViewCells to every XML element of xml_data whose name is "tableViewCell"" returns an empty array.

    ----------

    I kind of like using AppleScript for things like this.
     
  5. macrumors 603

    Joined:
    Aug 9, 2009
    #5
    The first step for someone who wants to assist you would be for them to replicate what you're seeing. To do that, they would need the same data you have. That is, the same XML file or files.

    Without the same data, no one can replicate what you're seeing, and no one can explain what you're seeing because no one knows the structure of your XML. So unless you post your actual XML data, no one can explain it.

    This is what I was trying to get at when I asked you the question, "What would someone else need in order to start finding the problem for you?". The answer is, they would need your XML data. You've already posted your AppleScript code, but without the XML that code is trying to act on, the code by itself is useless.
     
  6. thread starter macrumors 65816

    Joined:
    Mar 27, 2009
    #6
    I don't really think I need to do that. You could probably replicate it by making some arbitrary XMLL file with a bunch of elements with the name "tableViewCell"

    Anyways, I'm currently working on some code that will do the trick. I found out that I can tell an XML element to do whatever I want it to. So the code I'm building will accomplish the same task by removing all XML elements except the one it's currently working with, then putting them back in.
     
  7. macrumors 603

    Joined:
    Aug 9, 2009
    #7
    No one knows what your XML tags are. We could guess what the proper XML structure is based on your AppleScript code, but why?

    It's up to you to post the code and data that causes the problem. If you want to make (and test) an example XML file with the requisite structure that shows the problem, then please do that and post the fail-case.

    http://www.mikeash.com/getting_answers.html
     
  8. thread starter macrumors 65816

    Joined:
    Mar 27, 2009
    #8
    OK, I've hit a roadblock.

    Like I said before, I need to isolate one element from the pack at a time. To help me do that, I have a function I can use to remove a piece of text from a string and output the modified string. Obviously, this function doesn't work on XML files. This function is to be called by another function, which will go through all the elements of a given name and tell them to run the code I want them to run.

    UPDATE: I'm going in the wrong direction.
     
  9. thread starter macrumors 65816

    Joined:
    Mar 27, 2009
    #9
    Update on my progress toward a solution:

    Code:
    on removeText(parentString, childString, occurrencesToExclude)
    	set oldDelims to AppleScript's text item delimiters
    	set AppleScript's text item delimiters to childString
    	set textItems to every text item of parentString
    	set AppleScript's text item delimiters to oldDelims
    	if occurrencesToExclude is not missing value then
    		repeat with I from 1 to length of textItems
    			if occurrencesToExclude contains I then
    				set item I of textItems to item I of textItems & childString
    			end if
    		end repeat
    	end if
    	set newString to textItems as string
    	return newString
    end removeText
    on tellEveryXMLElementWithName(elementContainer, elementName, code)
    	set XMLContents to (read elementContainer)
    	set newContainer to removeText(XMLContents, elementName, 1)
    	tell elementName
    		run script code
    	end tell
    end tellEveryXMLElementWithName
    
    set the_file to (choose file without invisibles) as string
    tell application "System Events"
    	set file1 to XML file the_file
    	tell file1
    		set code to "set X to 0"
    		my tellEveryXMLElementWithName(file1, "tableViewCell", code)
    	end tell
    end tell
    
    It seems I can't perform a "read" operation on an XML file. What I want to do is create a function that others can use without doing anything besides calling it.
     
  10. ytk
    macrumors regular

    Joined:
    Jul 8, 2010
    #10
    And two weeks later, you're still struggling to debug a program that would take ten minutes to write in some other language. Suit yourself! :p
     
  11. thread starter macrumors 65816

    Joined:
    Mar 27, 2009
    #11
    I have Xcode. I just don't want to build a full-on app with GUI and everything, as my script won't be very fancy. I also don't want to download something else to do this.
     
  12. ytk
    macrumors regular

    Joined:
    Jul 8, 2010
    #12
    You don't have to do any of that. You can write the program in Ruby, Python, or Perl (all of which are already installed on every single Mac out there), and simply execute it from the command line. It doesn't need to have a GUI or anything at all. Or you can easily bake it into an application using Platypus, which lets you turn any program or shell script into a launchable app (with drag and drop support and everything) with just a few clicks. Heck, you can even call AppleScript from within your program, in case you want to use the communication functions or the rudimentary dialog boxes it provides.

    AppleScript has its uses. I use it all the time, actually—mainly calling it from within Ruby scripts to automate tedious tasks, much like what you're doing now. But trying to make AppleScript do something outside of the very narrow range of things it's good for is just torturing yourself for no good reason.
     
  13. thread starter macrumors 65816

    Joined:
    Mar 27, 2009
    #13
    None of which I'm familiar with. I'm not much of a desktop programmer, anyway. I'm more of a Web & mobile app developer. In fact, as I stated, I'm working on a recipe app. I figure this little project may help me in the future, if not right now. I'd do the work myself, but doing the work by hand is boring. Building an app that will do the work for me is fun. And I can share the app with others so they don't have to do that work themselves. (Don't worry, guys, it'll be free of cost and maybe I'll put a GNU GPL on it.)
     
  14. ytk
    macrumors regular

    Joined:
    Jul 8, 2010
    #14
    No time like the present to learn. Any of those languages would be useful for a web developer, and Ruby can be used to write and deploy mobile apps for iOS. And like I said, if you'd started learning the language back when you first posted about the problem, you'd have it solved by now. :D Based on the problem you're describing, we're really only talking about maybe 10 lines of code here.

    But go ahead and use AppleScript if you really want to. It's just not really well suited for this task. It's a bit like using a hammer to drive a screw—you can probably get it to work, but the result will be ugly, unreliable, and a pain to fix if something goes wrong.
     

Share This Page