解放区在住氷翠 緑の閃光
【解放区在住氷翠】デスクトップアプリケーション

【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データを書き込みするプログラムを作ろう。

コメントを残す

メールアドレスが公開されることはありません。