Swift 5:按顺序从XML解码元素

时间:2019-04-29 03:24:23

标签: swift xml codable decodable

我想使用XMLCoder library(使用Codable协议)解码以下XML:

<?xml version="1.0" encoding="UTF-8"?>
<container>
    <p>
        <run>
            <id>1518</id>
            <text>I am answering it again.</text>
        </run>
        <properties>
            <id>431</id>
            <title>A Word About Wake Times&#xD;</title>
        </properties>
        <br/>
        <run>
            <id>1519</id>
            <text>Hello!</text>
        </run>
    </p>
    <p>
        <run>
            <id>1520</id>
            <text>I am answering it again.</text>
        </run>
    </p>
</container>

但是非常重要的一点是将元素的顺序保持在p元素内。例如,在第一个p元素中有一个run,然后是properties,然后是br,最后是另一个run

这不能用基本结构实现,因为它将尝试从与数组相同的类型中解码元素,并且不会保持一般顺序。我也尝试使用enum,但存在相同的问题。

您知道实现此目标的某种方法吗?

当前无效代码:

struct Container: Decodable {
    let paragraphs: [Paragraph]

    enum CodingKeys: String, CodingKey {
        case paragraphs = "p"
    }
}

struct Run: Decodable {
    let id: Int
    let text: String
}

struct Properties: Decodable {
    let id: Int
    let title: String
}

struct Break: Decodable {

}

enum Entry: Decodable {
    case run(Run)
    case properties(Properties)
    case br(Break)

    private enum CodingKeys: String, CodingKey {
        case run, properties, br
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        // Here, when trying to decode the run elements:
        // DecodingError.typeMismatch: Expected to decode Dictionary<String, Any> but found SharedBox<UnkeyedBox> instead.
        if let run = try container.decodeIfPresent(Run.self, forKey: .run) {
            self = .run(run)
        }
        else if let properties = try container.decodeIfPresent(Properties.self, forKey: .properties) {
            self = .properties(properties)
        }
        else {
            self = .br(try container.decode(Break.self, forKey: .br))
        }
    }
}

struct Paragraph: Decodable {
    let entries: [Entry]

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        entries = try container.decode([Entry].self)
    }
}

do {
    let result = try XMLDecoder().decode(Container.self, from: data)
    for paragraph in result.paragraphs {
        print("Paragraph :")
        for entry in paragraph.entries {
            switch entry {
            case .run(let run):
                print("Run : \(run)")
            case .properties(let properties):
                print("Properties : \(properties)")
            case .br(let br):
                print("Break : \(br)")
            }
        }
        print("End paragraph")
    }
}
catch {
    print(error)
}

谢谢

0 个答案:

没有答案