Swift 4可编码解码:从字符串中解码URL

6

我目前正在尝试使用 PropertyListEncoder 和 Swift 4 的 Codable 协议来解码属性列表。

我尝试了一些基本类型(字符串,整数等),这些都可以正常工作,但我无法解码 URL。我阅读了多篇关于此主题的文章,我非常确定这应该可以正常工作。然而,以下示例在解码时失败并显示以下错误:

Expected to decode Dictionary<String, Any> but found a string/data instead.

我认为这在使用.json文件时会正确工作,但我没有找到有关JSONDecoderPropertyListDecoder对可解码类型支持不同的任何信息。这可能是由于解析器不兼容引起的吗?
我正在使用Xcode 9.1和Swift 4.0.2。
示例.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>web</key>
        <string>https://link.to</string>
    </dict>
</plist>

示例 Swift 代码:

struct Info: Codable {
   let web: URL
}

func loadInfo() {
    let propertiesDecoder = PropertyListDecoder()
    let data = try! Data(contentsOf:
        Bundle.main.url(forResource: "web", withExtension: "plist")!)

    try! propertiesDecoder.decode(Info.self, from: data)
}

感谢您的任何帮助!
3个回答

22

您可以将URL存储在属性列表中,并使用默认的可解码实现。

根据Swift标准库中Decodable协议init的实现,您必须将URL作为字典存储在Plist中,使用名为"relative"的键。可选地,您还可以在字典中包含一个名为"base"的嵌套URL字典。这两个值将在解码期间传递给URL(string: relative, relativeTo: base)构造函数。

例如:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
    <dict>
        <key>apiHost</key>
        <dict>
            <key>relative</key>
            <string>https://example.com/api/v0</string>
        </dict>
        <key>otherKey</key>
        <string>SomeValue</string>
       </dict>
 </array>
 </plist>

这个 Plist 将使用以下方法进行解码:

struct PlistItem: Codable {
    let apiHost: URL
    let otherKey: String
}
guard let filePath = Bundle.main.url(forResource: "Data", withExtension: "plist") else { return }
do {
    let data = try Data(contentsOf: filePath)
    let decoder = PropertyListDecoder()
    let values = try decoder.decode([PlistItem].self, data)
}
catch { }

谢谢提供链接。我注意到他们将SDK移动到另一个存储库https://github.com/apple/swift-corelibs-foundation/blob/main/Sources/Foundation/URL.swift#L1029 - CyberMew

2

我看到关键字web的值是一个String类型,因此我们需要保持匹配的类型。

但是要将字符串作为URL获取,则需要添加一个计算变量,并且可能需要对其进行优化,使变量成为惰性计算即只有在需要时才会计算一次。

编辑后的结构体应如下所示:

struct Info: Codable {
    let web: String

    lazy var url: URL? = { return URL(string: web) }()
}

2

假设您的JSON中的URL格式正确,您应该能够使用Swift 5手动将String解码为URL,方法如下:

let json = """
    {
        "name": "Dissent",
        "url": "https://www.dissentmagazine.org"
    }
"""

func hexAConversion(from hex: String)->Int {
    return Int(Int32(bitPattern: (UInt32(hex, radix: 16)!)))
}

let data = json.data(using: String.Encoding.utf8)!

let decoder = JSONDecoder()

struct CodeMe: Decodable {
  var name: String
  var url: URL?
    
  enum CodingKeys: String, CodingKey {
    case name, url
  }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
        self.url = URL(string: try container.decode(String.self, forKey: .url))
    }
  }

let codeMe = try! decoder.decode(CodeMe.self, from: data)

print(codeMe.url) // prints: Optional(https://www.dissentmagazine.org)

通过类似的技术,您还可以拥有自定义编码策略,使您的对象符合Codable而不是Decodable。请参见https://stackoverflow.com/a/65315622/713077获取更多详细信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接