将同一JSON元素的多次出现映射到Swift 4中具有Codable的对象列表

时间:2018-01-08 19:52:31

标签: json swift codable

我的来源是JSON-fied Wordpress Feed。这意味着,JSON多次出现相同的标记<item>

<channel>
    <item>
        <title>Titel 1</title>
    </item>
    <item>
        <title>Titel 2</title>
    </item>
</channel>

我还有两个结构ChannelItem。两者都实现了Codable协议。

频道

struct Channel: Codable {

    // Channel name
    let title: String?

    // All items (blog entries) from the channel
    let items = [Item]()

    enum CodingKeys: String, CodingKey {
        case title = "title"
        case items = "item"
    }

    init(from decoder: Decoder) throws {
        let values  = try decoder.container(keyedBy: CodingKeys.self)
        title       = try values.decode(String.self, forKey: .title)

        let itemValues = try values.nestedContainer(keyedBy: Item.CodingKeys, forKey: Feed.CodingKeys.items)
        items = try values.decode([Item.self], forKey: .title) // <-- Crash!!
    }

    func encode(to encoder: Encoder) throws {
        // ...
    }
}

我的问题是,我的let items = [Item]()永远不会被填满。我尝试了quickbytes.io中的示例,但它有另一种数据结构。

在我的经理中,我会打电话:

let jsonData = try? JSONSerialization.data(withJSONObject: jsonDict, options: .prettyPrinted)
try? decoder.decode(Feed.self, from: jsonData)

感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

您说您正在将该XML转换为JSON。我建议您编辑一个问题,向我们展示生成的JSON的样子。但让我们想象它最终看起来像:

{"channel":
    [
        {"title": "Title 1"},
        {"title": "Title 2"}
    ]
}

在这种情况下,你可以这样做:

struct Item: Codable {
    let title: String
}

struct Channel: Codable {
    let items: [Item]

    enum CodingKeys: String, CodingKey {
        case items = "channel"
    }
}

然后:

do {
    let channel = try JSONDecoder().decode(Channel.self, from: data)
    print(channel)
} catch {
    print(error)
}

但是,很明显,如果您的JSON看起来与我上面假设的不同,我们必须相应地更改实现。但是我们不能看到JSON实际上看起来像什么。

下面,在我的原始答案中,我将展示如何直接解析XML,绕过XML提要的JSON化。

我们不能说没有看到实际的JSON,但这一行显然是错误的:

items = try values.decode([Item.self], forKey: .title)

应该是:

items = try values.decode([Item].self, forKey: .items)

请注意]的位置,以及使用.items而不是.title

坦率地说,整个init(from:)看起来没什么必要,但是如果没有看到你的实际JSON看起来是什么就不可能说出来。

-

这是XML,而不是JSON。因此,您既不能使用JSONSerialization也不能使用JSONDecoder。因此,您可能希望使用XMLParser

let delegate = ChannelParserDelegate()
let parser = XMLParser(data: data)
parser.delegate = delegate
guard parser.parse(), let channel = delegate.channel else {
    print(parser.parserError ?? "Unknown error")
    return
}

print(channel)

其中

struct Channel {
    let items: [Item]
}

struct Item {
    let title: String
}

class ChannelParserDelegate: NSObject, XMLParserDelegate {
    var channel: Channel?
    private var items: [Item]?
    private var currentValue: String?
    private var values: [String: Any]?

    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
        if elementName == "channel" {
            items = []
        } else if elementName == "item" {
            values = [:]
        } else if elementName == "title" {
            currentValue = ""
        }
    }

    func parser(_ parser: XMLParser, foundCharacters string: String) {
        currentValue? += string
    }

    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        if elementName == "title" {
            values?[elementName] = currentValue
            currentValue = nil
        } else if elementName == "item" {
            items?.append(Item(title: values!["title"] as! String))
            values = nil
        } else if elementName == "channel" {
            channel = Channel(items: items!)
        }
    }

    func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
        channel = nil
        items = nil
    }
}

如果您最终获得的Item属性不仅仅是title,那么只需为title didStartElementdidEndElement中的JSONDecoder添加条款即可。 }。

为了完全公开,有一些WordPress插件可以为其生成JSON提要。那么,理论上你可以使用JSONSerialization(或者,如果你必须,XMLParser)。但是,如果您不想调整服务器配置,可以在客户端代码中使用----------------------- | Column 1 | Column 2 | ----------------------- | 1 | a | ----------------------- | 1 | b | ----------------------- | 2 | a | ----------------------- | 2 | a | ----------------------- | 3 | a | ----------------------- | 4 | b | ----------------------- | 4 | a | ----------------------- ,如上所述。