使用 Swifty 在字典中组合 JSON 和 String

Combine JSON and String in a dictionary with Swifty

我想在 Swifty 中创建一个具有以下形式的 JSON 对象:

{
    "store": {
        "id": {
            "test": "test"
        },
        "type": "retail",
        "name": "store1"

    }
}

有没有办法将字典中的类型与 Swifty(字符串和 JSON)结合使用?引号有效,但是当我尝试分配一个变量时,它会抱怨:Cannot assign value of type 'String' to type 'JSON?':

func jsonTest()->String {
    var storeJson = [String: JSON]()
    var someJson = JSON(["test":"test"])
    storeJson["id"] = someJson
    storeJson["type"] = "retail" // <-- works fine
    var name = "store1"
    storeJson["name"] = name // <-- Doesn't work
    var store = JSON(storeJson)
    return store.rawString()!
}

原因

storeJson["type"] = "retail"

工作方式不同于

storeJson["name"] = name

是因为第一个在代码中遵循不同的路径。具体来说,它在下面的扩展(source).

中使用了init(stringLiteral value: StringLiteralType)方法
extension JSON: Swift.StringLiteralConvertible {

    public init(stringLiteral value: StringLiteralType) {
        self.init(value)
    }

    public init(extendedGraphemeClusterLiteral value: StringLiteralType) {
        self.init(value)
    }

    public init(unicodeScalarLiteral value: StringLiteralType) {
        self.init(value)
    }
}

我们讨论了如何解决您的具体问题后,我会进一步解释。

可能的解决方案 #1:

storeJson["name"]?.string = name

输出:

{
  "id" : {
    "test" : "test"
  },
  "type" : "retail"
}

原因

storeJson["name"]?.string = name

没有像我们认为的那样工作是因为可选链接。现在,如果我们通过调试器 运行 这个,我们将看不到任何有意义的东西。事实上,我们会看到什么都没有。这有点令人担忧,可能意味着 storeJson["name"]nil,因此语句不再执行。让我们通过爆炸来验证我们的假设。我们将行更改为:

storeJson["name"]!.string = name

在这种情况下,使用您当前的代码,您可能会得到

fatal error: unexpectedly found nil while unwrapping an Optional value

如你所愿,因为 storeJson["name"] 实际上是 nil。因此,此解决方案不起作用

可能的解决方案#2:

正如您在回答中正确指出的那样,如果您添加 storeJson["name"] = JSON(name),您将获得所需的行为:

func jsonTest()->String {
    var storeJson = [String: JSON]()
    var someJson = JSON(["test":"test"])
    storeJson["id"] = someJson
    storeJson["type"] = "retail" // <-- works fine
    var name = "store1"
    storeJson["name"] = JSON(name) // <-- works!
    var store = JSON(storeJson)
    return store.rawString()!
}

输出:

{
  "id" : {
    "test" : "test"
  },
  "name" : "store1",
  "type" : "retail"
}

太棒了!因此,此解决方案有效!现在,稍后在您的代码中,您可以根据需要使用 .string 等更改它。

说明

回到字符串文字起作用的原因。您会注意到 init 中有

self.init(value)

它穿过对象init, which then goes through the case statement

        ...
        case let string as String:
            _type = .String
            self.rawString = string
        ...

当您调用 storeJson["name"] = JSON(name) 时,您将跳过 StringLiteralType init 并直接进入 switch.

因此,您可以互换

storeJson["type"] = "retail"

storeJson["type"] = JSON("retail")

事实证明它可以改变:

storeJson["name"] = name

storeJson["name"] = JSON(name)