Swift Codable-如何对自定义数组进行编码

问题描述 投票:1回答:2

这是我的JSON情况

      {
                 "image_id": 11101,
                 "image_source_id": 9,
                 "image_author": "",
                 "image_copyright": "",
                 "image_format_list": [{
                         "image_format": {
                             "image_url": "https://static.musixmatch.com/images-storage/mxmimages/1/0/1/1/1/11101_2.jpg",
                             "image_format_id": 2,
                             "width": 150,
                             "height": 150
                         }
                     },
                     {
                         "image_format": {
                             "image_url": "https://static.musixmatch.com/images-storage/mxmimages/1/0/1/1/1/11101_16.jpg",
                             "image_format_id": 16,
                             "width": 451,
                             "height": 500
                         }
                     }
                 ]
             }

我正确地将自定义对象解码为两个不同的类:MXMImage和MXMImageFormat。但我不知道如何重新编码对象以重建相同的JSON

这是我的代码:

    struct MXMImage : Decodable, Encodable, Equatable {
        let imageId: Int
        let imageSourceId: Int
        let imageAuthor: String?
        let imageCopyright: String?
        let imageFormatList: [MXMImageFormat]?

        enum CodingKeys: String, Swift.CodingKey {
            case imageId
            case imageSourceId
            case imageAuthor
            case imageCopyright
            case imageFormatList

            enum ImageFormatListKey: String, CodingKey {
                case imageFormat
            }
        }

        public init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            imageId = try (container.decodeIfPresent(Int.self, forKey: .imageId) ?? 0)
            imageSourceId = try (container.decodeIfPresent(Int.self, forKey: .imageSourceId) ?? 0)
            imageAuthor = try? container.decodeIfPresent(String.self, forKey: .imageAuthor)
            imageCopyright = try? container.decodeIfPresent(String.self, forKey: .imageCopyright)

            var imagesFormatListContainer = try container.nestedUnkeyedContainer(forKey: .imageFormatList)
            var imagesList:[MXMImageFormat] = []
            while !imagesFormatListContainer.isAtEnd {
                let imageFormatContainer = try imagesFormatListContainer.nestedContainer(keyedBy: CodingKeys.ImageFormatListKey.self)
                let imageFormat = try? imageFormatContainer.decode(MXMImageFormat.self, forKey: .imageFormat)
                if let imageFormat = imageFormat {
                    imagesList.append(imageFormat)
                }
            }
            self.imageFormatList = imagesList
        }

        public func encode(to encoder: Encoder) throws {
            var container = encoder.container(keyedBy: CodingKeys.self)

            try container.encodeIfPresent(imageId, forKey: .imageId)
            try container.encodeIfPresent(imageSourceId, forKey: .imageSourceId)
            try container.encodeIfPresent(imageAuthor, forKey: .imageAuthor)
            try container.encodeIfPresent(imageCopyright, forKey: .imageCopyright)

            var imageContainer = container.nestedUnkeyedContainer(forKey: .imageFormatList)
            try imageFormatList?.forEach { imgFormat in
                var nested = imageContainer.nestedContainer(keyedBy: CodingKeys.ImageFormatListKey.self)
                let data = try imgFormat.encoded()
                try nested.encode(data, forKey: .imageFormat)

            }
        }
    }

尤其是,我不知道如何在键image_format中重新缩进MXMImageFormat对象,然后对自定义数组进行编码。有可能这样做吗?在此先感谢

ios json swift codable
2个回答
2
投票

而不是nestedContainers,您可以解码/编码[[String:MXMImageFormat]]数组并映射它

struct MXMImage : Codable, Equatable {
    let imageId: Int
    let imageSourceId: Int
    let imageAuthor: String?
    let imageCopyright: String?
    let imageFormatList: [MXMImageFormat]?

    private enum CodingKeys : String, CodingKey { case imageId,  imageSourceId,  imageAuthor, imageCopyright, imageFormatList}

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        imageId = try container.decode(Int.self, forKey: .imageId)
        imageSourceId = try container.decode(Int.self, forKey: .imageSourceId)
        imageAuthor = try container.decodeIfPresent(String.self, forKey: .imageAuthor)
        imageCopyright = try container.decodeIfPresent(String.self, forKey: .imageCopyright)
        if let imageFormatListData = try container.decodeIfPresent([[String:MXMImageFormat]].self, forKey: .imageFormatList) {
            imageFormatList = imageFormatListData.compactMap{$0["image_format"]}
        } else {
            imageFormatList = nil
        }
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(imageId, forKey: .imageId)
        try container.encode(imageSourceId, forKey: .imageSourceId)
        try container.encodeIfPresent(imageAuthor, forKey: .imageAuthor)
        try container.encodeIfPresent(imageCopyright, forKey: .imageCopyright)
        if let imageFormatListData = imageFormatList {
            try container.encode(imageFormatListData.map{["image_format":$0]}, forKey: .imageFormatList)
        }
    }
}

struct MXMImageFormat : Codable, Equatable {
    let imageUrl : URL
    let imageFormatId, width, height : Int
}

0
投票

假设MXMImageFormat是这样的:

struct MXMImageFormat : Codable {
    let imageUrl: String
    let imageFormatId: Int
    let width: Int
    let height: Int
}

我认为您对此考虑过多。您可以这样做:

try imageFormatList?.forEach { imgFormat in
    var nested = imageContainer.nestedContainer(keyedBy: CodingKeys.ImageFormatListKey.self)
    try nested.encode(imgFormat, forKey: .imageFormat)
}

因为encode接受任何Encodable,包括imgFormat。实际上,您实际上不需要先转换为Data(至少这就是您似乎要尝试转换的原因)。

一些测试代码:

let json = """
{
   "image_id": 11101,
   "image_source_id": 9,
   "image_author": "",
   "image_copyright": "",
   "image_format_list": [{
           "image_format": {
               "image_url": "https://static.musixmatch.com/images-storage/mxmimages/1/0/1/1/1/11101_2.jpg",
               "image_format_id": 2,
               "width": 150,
               "height": 150
           }
       },
       {
           "image_format": {
               "image_url": "https://static.musixmatch.com/images-storage/mxmimages/1/0/1/1/1/11101_16.jpg",
               "image_format_id": 16,
               "width": 451,
               "height": 500
           }
       }
   ]
}
""".data(using: .utf8)!

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let model = try! decoder.decode(MXMImage.self, from: json)
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
let string = String(data: try! encoder.encode(model), encoding: .utf8)!
print(string) // this should be the same JSON as the one in the string literal

还请注意,在encode中,您不必在此处使用try?

while !imagesFormatListContainer.isAtEnd {
    let imageFormatContainer = try imagesFormatListContainer.nestedContainer(keyedBy: CodingKeys.ImageFormatListKey.self)
    // here vvvvvvv
    let imageFormat = try? imageFormatContainer.decode(MXMImageFormat.self, forKey: .imageFormat)
    if let imageFormat = imageFormat {
        imagesList.append(imageFormat)
    }
}

0
投票
import Foundation

let jsonData = """
{
    "image_id": 11101,
    "image_source_id": 9,
    "image_author": "",
    "image_copyright": "",
    "image_format_list": [{
            "image_format": {
                "image_url": "https://static.musixmatch.com/images-storage/mxmimages/1/0/1/1/1/11101_2.jpg",
                "image_format_id": 2,
                "width": 150,
                "height": 150
            }
        },
        {
            "image_format": {
                "image_url": "https://static.musixmatch.com/images-storage/mxmimages/1/0/1/1/1/11101_16.jpg",
                "image_format_id": 16,
                "width": 451,
                "height": 500
            }
        }
    ]
}
"""
let json = jsonData.data(using: .utf8)!

struct MXMImage: Codable {
    var imageId: Int
    var imageSourceId: Int
    var imageAuthor: String?
    var imageCopyright: String?
    var imageFormatList: [MXMImageFormat]?

    enum CodingKeys: String, CodingKey {
        case imageId = "image_id"
        case imageSourceId = "image_source_id"
        case imageAuthor = "image_author"
        case imageCopyright = "image_copyright"
        case imageFormatList = "image_format_list"
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        imageId = try container.decode(Int.self, forKey: .imageId)
        imageSourceId = try container.decode(Int.self, forKey: .imageSourceId)
        imageAuthor = try container.decodeIfPresent(String.self, forKey: .imageAuthor)
        imageCopyright = try container.decodeIfPresent(String.self, forKey: .imageCopyright)
        if let imageFormatListData = try container.decodeIfPresent([[String:MXMImageFormat]].self, forKey: .imageFormatList) {
            imageFormatList = imageFormatListData.compactMap{$0["image_format"]}
        } else {
            imageFormatList = nil
        }
    }
}

struct MXMImageFormat: Codable {
    var imageUrl: String
    var imageFormatId: Int
    var width: Int
    var height: Int

    enum CodingKeys: String, CodingKey {
        case imageUrl = "image_url"
        case imageFormatId = "image_format_id"
        case width = "width"
        case height = "height"
    }
}

if let results = try? JSONDecoder().decode(MXMImage.self, from: json) {
    print(results)
}

打印输出:

MXMImage(
  imageId: 11101, 
  imageSourceId: 9, 
  imageAuthor: Optional(""), 
  imageCopyright: Optional(""), 
  imageFormatList: Optional([
      SwiftPlayground.MXMImageFormat(
          imageUrl: "https://static.musixmatch.com/images-storage/mxmimages/1/0/1/1/1/11101_2.jpg",

          imageFormatId: 2, 
          width: 150, 
          height: 150), 
      SwiftPlayground.MXMImageFormat(
          imageUrl: "https://static.musixmatch.com/images-storage/mxmimages/1/0/1/1/1/11101_16.jpg",

          imageFormatId: 16, 
          width: 451, 
          height: 500)
  ])
)

您可以尝试在http://online.swiftplayground.run/中运行它

© www.soinside.com 2019 - 2024. All rights reserved.