XMLノードをNSBrowserで表示
NSBrowser の情報があまり見つからなかったので、XMLデータのノード名を表示させた方法をメモっておく。
まずは、NSXMLDocument オブジェクト(インスタンス変数)を作る。ここでは、initWithContentsOfURL:options:error: でファイルから作る。initWithXMLString:options:error: で、読み込んだテキストファイルから XML 形式の文字列を作って、それを使って作ってもいい。
@xmlDoc = NSXMLDocument.alloc.initWithContentsOfURL(url,options:NSXMLDocumentTidyXML,error:nil)
NSBrowser の UI は、Xcode で置きたいビューに配置する。
配置したら、Browser を使いたいクラスで outlet (@browser) として繋ぎ、そのクラスを @browser の delegate にする。
delegate にしたクラスに用意する必要があるのは、browser:numberOfRowsInColumn: と browser:willDisplayCell:atRow:column: の 2 つ。前者は、表示するカラムで用意する必要がある行数を返し、もう一つは表示するデータを返す。NSTableViewDelegate の numberOfRowsInTableView: と tableView:objectValueForTableColumn:row: に相当するようもの。
ここでは、次のようなスクリプトを書いてみた。
def browser(browser,numberOfRowsInColumn:col)
if @xmlDoc
if col == 0
@xmlDoc.rootElement.childCount
else
@xmlDoc.rootElement.nodesForXPath("/#{browser.path.split("/")[0..col].join("/")}",error:nil)[0].childCount
end
else
0
end
end
def browser(browser,willDisplayCell:cell,atRow:row,column:col)
if @xmlDoc
if col == 0
item = @xmlDoc.rootElement.children[row]
cell.setLeaf(true) if item.childCount == 0
cell.setStringValue(item.name)
else
item = @xmlDoc.rootElement.nodesForXPath("/#{browser.path.split("/")[0..col].join("/")}",error:nil)[0].children[row]
cell.setLeaf(true) if item.childCount == 0
cell.setStringValue(item.name)
end
end
end
まず、browser:numberOfRowsInColumn: では、@xmlDoc が存在しなければ、0 を返し、存在すれば、行数を返すようにしてあるが、2 列(カラム)目以降はその前のカラムで選択された項目で行数が決まるので、その処理をするためにちょっと面倒臭いことしている。
col == 0 つまり、一つ目のカラムの時は、ルートの要素の子ノードの数なので、@xmlDoc.rootElement.childCount で、その数を取り出している。それ以降は、browser.path で、最後に選択した要素のパスを取り出して、一つ手前の要素までの部分を抜き出して XPath として rootElement に対し nodesForXPath でそのパスのノードを探してその子ノード数を返す。具体的には、/ で分割して、一つ手前の部分まで、つまり、col の値までを抜き出して / で結合している。さらに先頭に / をつけているのは、XPath では「//要素」でその要素を直接指定できることを利用している。最後の要素で直接指定すればいいものでもあるが、同じ要素がある場合に面倒なのでこうしてある。こうして、クリックして選択された要素の子ノード数がわかるのでそれを返している。
browser:willDisplayCell:atRow:column: では、browser:numberOfRowsInColumn: で返した子ノードそれぞれにラベルとなる物を返すのであるが、(NS)String を返すのではなく、cell の中身を返すので、cell.setStringValue() で cell に文字列を設定する。何行目かという情報は、row に入るのでそれを利用する。また、その要素の子ノードがない場合は、その次のカラムにデータを表示しないように、cell.setLeaf(true) で末端であるという情報を返している。
あと、ブラウザで cell をクリックした時に、その cell に結びつけた要素の情報を得るために、browserCellSelected: というメソッドを追加した。setSendsActionOnArrowKeys(bool) は、カーソルキーでも操作ができるようにするための設定。
def awakeFromNib
@browser.setTarget(self)
@browser.setAction("browserCellSelected:")
@browser.setSendsActionOnArrowKeys(true)
end
def browserCellSelected(sender)
end
まだまだ改善の余地はあると思うが、とりあえず動いているので、当面はこれで行く。
最後の上記のスクリプトでどんな表示になるかのサンプル。