Swift에서 Enum Decodable을 만들려면 어떻게 해야 합니까?
enum PostType: Decodable {
init(from decoder: Decoder) throws {
// What do i put here?
}
case Image
enum CodingKeys: String, CodingKey {
case image
}
}
이걸 완성하려면 어떻게 해야 하나요?또한, 제가 변경했다고 가정해 보겠습니다.case
대상:
case image(value: Int)
디코딩할 수 있도록 하려면 어떻게 해야 합니까?
(작동하지 않는) 전체 코드입니다.
let jsonData = """
{
"count": 4
}
""".data(using: .utf8)!
do {
let decoder = JSONDecoder()
let response = try decoder.decode(PostType.self, from: jsonData)
print(response)
} catch {
print(error)
}
}
}
enum PostType: Int, Codable {
case count = 4
}
또한 이와 같은 열거형을 어떻게 처리할 것입니까?
enum PostType: Decodable {
case count(number: Int)
}
그것은 꽤 쉽습니다, 그냥 사용하세요.String
또는Int
암시적으로 할당된 원시 값.
enum PostType: Int, Codable {
case image, blob
}
image
는 인딩됨에로 됩니다.0
그리고.blob
1
또는
enum PostType: String, Codable {
case image, blob
}
image
는 인딩됨에로 됩니다."image"
그리고.blob
"blob"
다음은 사용 방법의 간단한 예입니다.
enum PostType : Int, Codable {
case count = 4
}
struct Post : Codable {
var type : PostType
}
let jsonString = "{\"type\": 4}"
let jsonData = Data(jsonString.utf8)
do {
let decoded = try JSONDecoder().decode(Post.self, from: jsonData)
print("decoded:", decoded.type)
} catch {
print(error)
}
갱신하다
iOS 13.3+ 및 macOS 15.1+에서는 컬렉션 유형에 포함되지 않은 단일 JSON 값인 fragment를 인코딩/디코딩할 수 있습니다.
let jsonString = "4"
let jsonData = Data(jsonString.utf8)
do {
let decoded = try JSONDecoder().decode(PostType.self, from: jsonData)
print("decoded:", decoded) // -> decoded: count
} catch {
print(error)
}
Swift 5.5+에서는 추가 코드 없이 관련 값으로 열거형을 인코딩/디코딩할 수도 있습니다.값이 사전에 매핑되고 관련된 각 값에 대해 매개 변수 레이블을 지정해야 합니다.
enum Rotation: Codable {
case zAxis(angle: Double, speed: Int)
}
let jsonString = #"{"zAxis":{"angle":90,"speed":5}}"#
let jsonData = Data(jsonString.utf8)
do {
let decoded = try JSONDecoder().decode(Rotation.self, from: jsonData)
print("decoded:", decoded)
} catch {
print(error)
}
된 유형의 은 있는열다을음방같거형법만이드는과관련형유이▁to▁how방법▁with▁toums▁en▁make▁conform는.Codable
은 @ @Howard Lovatt @Howard Lovatt의 답변을 만드는 것을 .PostTypeCodableForm
구화하사니다합용을 합니다.KeyedEncodingContainer
Apple에서 속성으로 제공한 유형Encoder
그리고.Decoder
보일러 플레이트를 줄여줍니다.
enum PostType: Codable {
case count(number: Int)
case title(String)
}
extension PostType {
private enum CodingKeys: String, CodingKey {
case count
case title
}
enum PostTypeCodingError: Error {
case decoding(String)
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
if let value = try? values.decode(Int.self, forKey: .count) {
self = .count(number: value)
return
}
if let value = try? values.decode(String.self, forKey: .title) {
self = .title(value)
return
}
throw PostTypeCodingError.decoding("Whoops! \(dump(values))")
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .count(let number):
try container.encode(number, forKey: .count)
case .title(let value):
try container.encode(value, forKey: .title)
}
}
}
이 코드는 Xcode 9b3에서 작동합니다.
import Foundation // Needed for JSONEncoder/JSONDecoder
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let decoder = JSONDecoder()
let count = PostType.count(number: 42)
let countData = try encoder.encode(count)
let countJSON = String.init(data: countData, encoding: .utf8)!
print(countJSON)
// {
// "count" : 42
// }
let decodedCount = try decoder.decode(PostType.self, from: countData)
let title = PostType.title("Hello, World!")
let titleData = try encoder.encode(title)
let titleJSON = String.init(data: titleData, encoding: .utf8)!
print(titleJSON)
// {
// "title": "Hello, World!"
// }
let decodedTitle = try decoder.decode(PostType.self, from: titleData)
스위프트는 던질 것입니다..dataCorrupted
알 수 없는 열거값이 발견되면 오류가 발생합니다.데이터가 서버에서 전송되는 경우 언제든지 알 수 없는 열거형 값을 보낼 수 있습니다(버그 서버 측, API 버전에 추가된 새로운 유형, 이전 버전의 앱이 사례를 정상적으로 처리하기를 원하는 경우 등). 준비를 하고 "방어 스타일"을 코드화하여 열거형을 안전하게 디코딩하는 것이 좋습니다.
다음은 관련 값을 포함하거나 포함하지 않는 방법에 대한 예입니다.
enum MediaType: Decodable {
case audio
case multipleChoice
case other
// case other(String) -> we could also parametrise the enum like that
init(from decoder: Decoder) throws {
let label = try decoder.singleValueContainer().decode(String.self)
switch label {
case "AUDIO": self = .audio
case "MULTIPLE_CHOICES": self = .multipleChoice
default: self = .other
// default: self = .other(label)
}
}
}
그리고 그것을 둘러싸는 구조에서 사용하는 방법:
struct Question {
[...]
let type: MediaType
enum CodingKeys: String, CodingKey {
[...]
case type = "type"
}
extension Question: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
[...]
type = try container.decode(MediaType.self, forKey: .type)
}
}
@@Toka에 원시 하여 @Toka 에하기응, 당을한가추을, 의▁a 없이 ▁without를 수 .switch
:
enum MediaType: String, Decodable {
case audio = "AUDIO"
case multipleChoice = "MULTIPLE_CHOICES"
case other
init(from decoder: Decoder) throws {
let label = try decoder.singleValueContainer().decode(String.self)
self = MediaType(rawValue: label) ?? .other
}
}
생성자를 리팩터링할 수 있는 사용자 정의 프로토콜을 사용하여 확장할 수 있습니다.
protocol EnumDecodable: RawRepresentable, Decodable {
static var defaultDecoderValue: Self { get }
}
extension EnumDecodable where RawValue: Decodable {
init(from decoder: Decoder) throws {
let value = try decoder.singleValueContainer().decode(RawValue.self)
self = Self(rawValue: value) ?? Self.defaultDecoderValue
}
}
enum MediaType: String, EnumDecodable {
static let defaultDecoderValue: MediaType = .other
case audio = "AUDIO"
case multipleChoices = "MULTIPLE_CHOICES"
case other
}
또한 잘못된 열거값이 지정된 경우 값을 기본값으로 지정하는 대신 오류를 발생시키는 경우 쉽게 확장할 수 있습니다.이 변경 사항이 있는 요지는 https://gist.github.com/stephanecopin/4283175fabf6f0cdaf87fef2a00c8128 에서 확인할 수 있습니다.
코드는 Swift 4.1/X 코드 9.3을 사용하여 컴파일 및 테스트되었습니다.
@proxpero 응답의 변형인 에스테르는 디코더를 다음과 같이 공식화하는 것입니다.
public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
guard let key = values.allKeys.first else { throw err("No valid keys in: \(values)") }
func dec<T: Decodable>() throws -> T { return try values.decode(T.self, forKey: key) }
switch key {
case .count: self = try .count(dec())
case .title: self = try .title(dec())
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .count(let x): try container.encode(x, forKey: .count)
case .title(let x): try container.encode(x, forKey: .title)
}
}
이를 통해 컴파일러는 대소문자를 철저히 검증할 수 있으며, 인코딩된 값이 키의 예상 값과 일치하지 않는 경우에 대한 오류 메시지를 억제하지 않습니다.
실제로 위의 답변은 정말 훌륭하지만, 지속적으로 개발되는 클라이언트/서버 프로젝트에서 많은 사람들이 필요로 하는 것에 대한 세부 정보가 누락되어 있습니다.우리는 백엔드가 시간이 지남에 따라 지속적으로 진화하는 동안 앱을 개발합니다. 이는 일부 열거형 사례가 이러한 진화를 변화시킬 것임을 의미합니다.따라서 우리는 알려지지 않은 사례를 포함하는 열거형 디코딩 전략이 필요합니다.그렇지 않으면 배열이 포함된 개체를 디코딩할 수 없습니다.
제가 한 일은 아주 간단합니다.
enum Direction: String, Decodable {
case north, south, east, west
}
struct DirectionList {
let directions: [Direction]
}
extension DirectionList: Decodable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
var directions: [Direction] = []
while !container.isAtEnd {
// Here we just decode the string from the JSON which always works as long as the array element is a string
let rawValue = try container.decode(String.self)
guard let direction = Direction(rawValue: rawValue) else {
// Unknown enum value found - ignore, print error to console or log error to analytics service so you'll always know that there are apps out which cannot decode enum cases!
continue
}
// Add all known enum cases to the list of directions
directions.append(direction)
}
self.directions = directions
}
}
보너스 : 구현 숨기기 > 컬렉션으로 만들기
구현 세부 정보를 숨기는 것은 항상 좋은 생각입니다.이를 위해서는 코드가 조금만 더 필요합니다.요령은 순응하는 것입니다.DirectionsList
로.Collection
그리고 당신의 내부를 만듭니다.list
배열 개인:
struct DirectionList {
typealias ArrayType = [Direction]
private let directions: ArrayType
}
extension DirectionList: Collection {
typealias Index = ArrayType.Index
typealias Element = ArrayType.Element
// The upper and lower bounds of the collection, used in iterations
var startIndex: Index { return directions.startIndex }
var endIndex: Index { return directions.endIndex }
// Required subscript, based on a dictionary index
subscript(index: Index) -> Element {
get { return directions[index] }
}
// Method that returns the next index when iterating
func index(after i: Index) -> Index {
return directions.index(after: i)
}
}
맞춤형 컬렉션 준수에 대한 자세한 내용은 John Sundell의 블로그 게시물에서 확인할 수 있습니다. https://medium.com/ @johnsundell/creatoring-custom-collections-in-bba344e25d0b0
당신은 당신이 원하는 것을 할 수 있지만, 그것은 약간 관련이 있습니다 :(
import Foundation
enum PostType: Codable {
case count(number: Int)
case comment(text: String)
init(from decoder: Decoder) throws {
self = try PostTypeCodableForm(from: decoder).enumForm()
}
func encode(to encoder: Encoder) throws {
try PostTypeCodableForm(self).encode(to: encoder)
}
}
struct PostTypeCodableForm: Codable {
// All fields must be optional!
var countNumber: Int?
var commentText: String?
init(_ enumForm: PostType) {
switch enumForm {
case .count(let number):
countNumber = number
case .comment(let text):
commentText = text
}
}
func enumForm() throws -> PostType {
if let number = countNumber {
guard commentText == nil else {
throw DecodeError.moreThanOneEnumCase
}
return .count(number: number)
}
if let text = commentText {
guard countNumber == nil else {
throw DecodeError.moreThanOneEnumCase
}
return .comment(text: text)
}
throw DecodeError.noRecognizedContent
}
enum DecodeError: Error {
case noRecognizedContent
case moreThanOneEnumCase
}
}
let test = PostType.count(number: 3)
let data = try JSONEncoder().encode(test)
let string = String(data: data, encoding: .utf8)!
print(string) // {"countNumber":3}
let result = try JSONDecoder().decode(PostType.self, from: data)
print(result) // count(3)
특징들
- 간편한 사용.디코딩 가능 인스턴스의 한 줄: line eg
let enum: DecodableEnum<AnyEnum>
- 표준 매핑 메커니즘으로 디코딩됩니다.
JSONDecoder().decode(Model.self, from: data)
- 알 수 없는 데이터를 수신하는 경우(예: 매핑)
Decodable
예기치 않은 데이터를 수신해도 개체에 장애가 발생하지 않습니다. - 손잡이/손잡이
mapping
또는decoding
오류들
세부 사항
- Xcode 12.0.1(12A7300)
- 스위프트 5.3
해결책
import Foundation
enum DecodableEnum<Enum: RawRepresentable> where Enum.RawValue == String {
case value(Enum)
case error(DecodingError)
var value: Enum? {
switch self {
case .value(let value): return value
case .error: return nil
}
}
var error: DecodingError? {
switch self {
case .value: return nil
case .error(let error): return error
}
}
enum DecodingError: Error {
case notDefined(rawValue: String)
case decoding(error: Error)
}
}
extension DecodableEnum: Decodable {
init(from decoder: Decoder) throws {
do {
let rawValue = try decoder.singleValueContainer().decode(String.self)
guard let layout = Enum(rawValue: rawValue) else {
self = .error(.notDefined(rawValue: rawValue))
return
}
self = .value(layout)
} catch let err {
self = .error(.decoding(error: err))
}
}
}
사용샘플
enum SimpleEnum: String, Codable {
case a, b, c, d
}
struct Model: Decodable {
let num: Int
let str: String
let enum1: DecodableEnum<SimpleEnum>
let enum2: DecodableEnum<SimpleEnum>
let enum3: DecodableEnum<SimpleEnum>
let enum4: DecodableEnum<SimpleEnum>?
}
let dictionary: [String : Any] = ["num": 1, "str": "blablabla", "enum1": "b", "enum2": "_", "enum3": 1]
let data = try! JSONSerialization.data(withJSONObject: dictionary)
let object = try JSONDecoder().decode(Model.self, from: data)
print("1. \(object.enum1.value)")
print("2. \(object.enum2.error)")
print("3. \(object.enum3.error)")
print("4. \(object.enum4)")
여기에는 여러 가지 좋은 접근법이 있지만, 하나 이상의 값을 가진 열거형을 논의하는 것을 본 적이 없습니다. 예를 들어 추론할 수는 있지만, 누군가 이에 대한 용도를 찾을 수도 있습니다.
import Foundation
enum Tup {
case frist(String, next: Int)
case second(Int, former: String)
enum TupType: String, Codable {
case first
case second
}
enum CodingKeys: String, CodingKey {
case type
case first
case firstNext
case second
case secondFormer
}
}
extension Tup: Codable {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
let type = try values.decode(TupType.self, forKey: .type)
switch type {
case .first:
let str = try values.decode(String.self, forKey: .first)
let next = try values.decode(Int.self, forKey: .firstNext)
self = .frist(str, next: next)
case .second:
let int = try values.decode(Int.self, forKey: .second)
let former = try values.decode(String.self, forKey: .secondFormer)
self = .second(int, former: former)
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .frist(let str, next: let next):
try container.encode(TupType.first, forKey: .type)
try container.encode(str, forKey: .first)
try container.encode(next, forKey: .firstNext)
case .second(let int, former: let former):
try container.encode(TupType.second, forKey: .type)
try container.encode(int, forKey: .second)
try container.encode(former, forKey: .secondFormer)
}
}
}
let example1 = Tup.frist("123", next: 90)
do {
let encoded = try JSONEncoder().encode(example1)
print(encoded)
let decoded = try JSONDecoder().decode(Tup.self, from: encoded)
print("decoded 1 = \(decoded)")
}
catch {
print("errpr = \(error.localizedDescription)")
}
let example2 = Tup.second(10, former: "dantheman")
do {
let encoded = try JSONEncoder().encode(example2)
print(encoded)
let decoded = try JSONDecoder().decode(Tup.self, from: encoded)
print("decoded 2 = \(decoded)")
}
catch {
print("errpr = \(error.localizedDescription)")
}
다음은 Swift에서 열거형 디코딩 가능한 방법의 간단한 예입니다.
샘플 JSON:
[
{
"title": "1904",
"artist": "The Tallest Man on Earth",
"year": "2012",
"type": "hindi"
},
{
"title": "#40",
"artist": "Dave Matthews",
"year": "1999",
"type": "english"
},
{
"title": "40oz to Freedom",
"artist": "Sublime",
"year": "1996",
"type": "english"
},
{
"title": "#41",
"artist": "Dave Matthews",
"year": "1996",
"type": "punjabi"
}
]
모델 구조:
struct Song: Codable {
public enum SongType: String, Codable {
case hindi = "hindi"
case english = "english"
case punjabi = "punjabi"
case tamil = "tamil"
case none = "none"
}
let title: String
let artist: String
let year: String
let type: SongType?
}
이제 JSON 파일을 구문 분석하고 데이터를 아래와 같은 노래 배열로 구문 분석할 수 있습니다.
func decodeJSON() {
do {
// creating path from main bundle and get data object from the path
if let bundlePath = Bundle.main.path(forResource: "sample", ofType: "json"),
let jsonData = try String(contentsOfFile: bundlePath).data(using: .utf8) {
// decoding an array of songs
let songs = try JSONDecoder().decode([Song].self, from: jsonData)
// printing the type of song
songs.forEach { song in
print("Song type: \(song.type?.rawValue ?? "")")
}
}
} catch {
print(error)
}
}
문의 사항이 있을 경우 아래에 설명해 주십시오.
언급URL : https://stackoverflow.com/questions/44580719/how-do-i-make-an-enum-decodable-in-swift
'programing' 카테고리의 다른 글
OpenXML 스프레드시트의 셀 스타일(스프레드시트)ML) (0) | 2023.05.14 |
---|---|
Excel에서 수식을 전체 열에 적용하기 위한 바로 가기 (0) | 2023.05.14 |
C#의 배열 목록 대 목록<> (0) | 2023.05.14 |
Postgre는 왜?SQL이 인덱스된 열에 대해 순차적 검색을 수행하시겠습니까? (0) | 2023.05.14 |
왜 도커 내에서 포트 80에서 aspnet core가 시작됩니까? (0) | 2023.05.14 |