【Swift】XMLデータを読み込みたい
2023-05-08
こんばんは、氷翠です。
これからアプリを作成していかないといけないので、アプリと別にデータをバックアップとるのに、XMLデータとして保管するのがいいかと思い、特にこのXMLデータを読み込むプログラムを勉強するしかないと。
ということで、プログラムを組んで見ました。
今回はXMLデータを読み込むだけなので、ボタンを配置して、リストによって表示させていくというだけのものになっています。
List {
// Text("管理ID : 名称 // 値") // こんな感じで表示していこう
ForEach(self.items) { item in
// データを1行の文字列にする
let a = item.itemNo+" // "+item.itemName+":"+item.value
// ここで出力
Text(a)
} // end foreach
} // end list
最終的にはこんな感じで表示させる。
<?xml version="1.0" encoding="UTF-8"?>
<root>
<items itemNo="1">
<itemName>Item Name A</itemName>
<value>value A</value>
</items>
<items itemNo="2">
<itemName>Item Name B</itemName>
<value>value B</value>
</items>
</root>
データはこんな感じで準備をした。
問題は別のファイルにしてあるクラス。
まずはそのファイルの中でデータの形を「struct」で定義しておく。
struct item: Identifiable {
var id : UUID // ユニークIDを利用する。ビューで繰り返し処理を行うために必要
var itemNo : String // 内容はINTだが、どの道文字列の一部になるのでStringにしておく
var itemName : String // 以下二つはもちろん文字列にしておく
var value : String
}
こんな感じで準備。
読み込みで前段階の準備。
class xmlRead: NSObject {
// 後にこのクラスを拡張するため
// その中で使う変数を定義していく
var parser : XMLParser!
var targetElem : String = ""
// FileManagerを利用するため、そのメソッドまでを短縮します
let fm = FileManager.default
// 解析後の配列変数
var items : [item] = []
// 要素の一つ分
var oneItem : item!
// NSObjectを使うときにこのinitを利用する場合は「override」とする
override init() {
super.init()
// データを読み込むパス(URL型)を定義する
guard let path = fm.urls(for: .desktopDirectory, in: .userDomainMask).first else {
fatalError("URL取得失敗")
}
// 上記のパスにファイル名を設定します
let fullURL = path.appendingPathComponent("test.xml")
// XML読み込むには通常とは違う方法で読み込む
if let parser = XMLParser(contentsOf: fullURL) {
self.parser = parser
self.parser.delegate = self
self.parser.parse()
} // end if
} // end init
}
このクラスの中に、後に記述していく拡張のクラス内で使う変数も定義していく。
更に「XMLParser」も利用するので、定義しておく。
で、ここでつかう「init」はすでに「NSObject」で使われているので、「override」で上書きする感じで定義していく。
そして、XMLデータを読み込むときは「XMLParser(contentsOf: URL型変数)」で読み込みをする。あとはdelegateで内部処理を行っていく。その内容が、この後で追加する「拡張」ってことになる。
extension xmlRead: XMLParserDelegate {
func parserDidStartDocument(_ parser: XMLParser) {
//XMLのパースの開始で呼ばれるメソッド
}
func parser(_ parser: XMLParser,
didStartElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String : String]) {
//XML中のタグの開始時に呼ばれる
if elementName == "items" {
// このタグがあった時に一つ分のデータを初期化する処理を行う
// このときにユニークIDも作成
let uid = UUID()
// 他のデータは空にしておく
// これがないと、データはnilとなるため、後にエラーになってしまう
self.oneItem = item(id: uid, itemNo: "", itemName: "", value: "")
// 属性は「attributeDict」というdictionary型になる
// optionalなのでString型に強制する
self.oneItem.itemNo = String(attributeDict["itemNo"]!)
} // end if
else {
// 他のメソッドでタグの名称を利用するため保管しておく
self.targetElem = elementName
} // end else
}
func parser(_ parser:XMLParser,foundCharacters string:String){
//タグ中のデータに対して呼ばれるメソッド
if self.targetElem == "itemName" && self.oneItem.itemName == "" {
self.oneItem.itemName = string
} // end if
else if self.targetElem == "value" && self.oneItem.value == "" {
self.oneItem.value = string
} // end if
else {
// 上記以外のタグの場合
self.targetElem = ""
} // end else
}
func parser(_ parser:XMLParser,
didEndElement elementName:String,
namespaceURI:String?,
qualifiedName qName:String?){
//XML中のタグの終了時に対して呼ばれる
if elementName == "items" {
// 大元のタグが終了するときに
// 一つ分のデータを上位の配列変数に追加する
self.items.append(self.oneItem)
} // end if
}
func parserDidEndDocument(_ parser:XMLParser){
//XMLのパースの終了に呼ばれるメソッド
}
}
ここではあまり説明はいらないかもしれないw
この後はXMLデータを書き込みするプログラムを作ろう。
コメントを残す