在Swift中使用不同的键从Firebase编写JSON

ubof19bj  于 2023-08-02  发布在  Swift
关注(0)|答案(1)|浏览(98)

我在Firebase RealtimeDatabase中有一个JSON:

{
  "users": {
    “vZxpxfRG9tNaZKruActA7KyEgwz2": {
      "avatar": "022",
      "friends": {
        "HCLnd41UluP27w1JjXiRK8RvUwt2": {
          "nickname": "randomNickname"
        },
        "RaZKru2jG97KyEgtN7w1JtAwzxfc": {
          "nickname": "randomNickname2"
        }
      },
      "username": "myUsername"
    }
  }
}

字符串
我试着把它编码/解码成这个User模型:

struct User: Codable {
    let username: String
    let avatar: String
    let friends: [UserFriend]?
}

struct UserFriend: Codable {
    let nickname: String?
}


如果User对象的friends为nil,它可以成功编码/解码,因为我给予了这样的引用路径:

let ref = Database.database(url: "{Firebase database URL}").reference().child("users/\(userID)")


但如果数据库中的friends属性不为nil,则无法解码。我无法在Swift中将数据转换为模型。
我显然做错了什么,可能是在Firebase数据结构和Swift的编码/解码上。
我应该将JSON结构从键/值对更改为数组,还是应该使用Swift成功地将JSON对象转换为我的自定义Swift“UserFriend”对象?

wd2eg0qa

wd2eg0qa1#

您可以编写一个属性 Package 器,它只提取JSON字典/对象的值,并忽略随机字符串的键。

@propertyWrapper
struct DictionaryValue<T: Codable>: Codable {
    let wrappedValue: [T]
    
    init(from decoder: Decoder) throws {
        // here I decode the json as a dictionary, but keep only the values
        let dict = try [String: T](from: decoder)
        wrappedValue = Array(dict.values)
    }
    
    func encode(to encoder: Encoder) throws {
        // you can only encode it this way,
        // since the keys are gone.
        // Consider only conforming to Decodable instead.
        try wrappedValue.encode(to: encoder)
    }
}

字符串
然后,您可以将其应用于根目录下的users键和friends键:

struct Root: Codable {
    @DictionaryValue var users: [User]
}
struct User: Codable {
    let username: String
    let avatar: String
    @DictionaryValue var friends: [UserFriend]
}

struct UserFriend: Codable {
    let nickname: String?
}

let root = try JSONDecoder().decode(Root.self, from: json)
print(root)


请注意,由于字典不是有序的,因此好友和用户的顺序可能与原始JSON中的顺序不同。
而且我假设所有的钥匙都在。如果没有friends时,JSON中完全没有friends键,则需要手动解码所有键:

init(from decoder: Decoder) throws {
    var container = try decoder.container(keyedBy: CodingKeys.self)
    username = try container.decode(String.self, forKey: .username)
    avatar = try container.decode(String.self, forKey: .avatar)
    if let friends = try container.decodeIfPresent([String: UserFriend].self, forKey: .friends)?.values {
        self.friends = Array(friends)
    }
}


或者显式地写一个DictionaryValue<UserFriend>?类型的属性(结果,* 不 * 使用属性 Package 器作为属性 Package 器),如下所示:

let _friends: DictionaryValue<UserFriend>?

var friends: [UserFriend]? {
    friendsWrapper?.wrappedValue
}

相关问题