Swift 4의 JSONDecoder를 사용하면 키가 누락된 경우 옵션 속성 대신 기본값을 사용할 수 있습니까?
스위프트 4를 했다.Codable
프로토콜입니다.★★★★★★★를 사용하는 JSONDecoder
이 아닌 로 하는 것 .Codable
JSON을 사용하다
제가 정말로 원하는 것은 json 값이나 기본값을 사용하는 것이기 때문에 클래스의 모든 속성을 옵션으로 만드는 것은 불필요한 번거로움으로 보입니다.(프로퍼티가 0이 되는 것을 원하지 않습니다.
방법이 있을까요?
class MyCodable: Codable {
var name: String = "Default Appleseed"
}
func load(input: String) {
do {
if let data = input.data(using: .utf8) {
let result = try JSONDecoder().decode(MyCodable.self, from: data)
print("name: \(result.name)")
}
} catch {
print("error: \(error)")
// `Error message: "Key not found when expecting non-optional type
// String for coding key \"name\""`
}
}
let goodInput = "{\"name\": \"Jonny Appleseed\" }"
let badInput = "{}"
load(input: goodInput) // works, `name` is Jonny Applessed
load(input: badInput) // breaks, `name` required since property is non-optional
하면 .init(from decoder: Decoder)
기본 구현을 사용하지 않고 원하는 유형의 메서드를 사용합니다.
class MyCodable: Codable {
var name: String = "Default Appleseed"
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let name = try container.decodeIfPresent(String.self, forKey: .name) {
self.name = name
}
}
}
,도 수 ,name
수원원 ( 원원원원원원원원원 ) :
class MyCodable: Codable {
let name: String
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let name = try container.decodeIfPresent(String.self, forKey: .name) {
self.name = name
} else {
self.name = "Default Appleseed"
}
}
}
또는
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Default Appleseed"
}
코멘트 참조:커스텀 확장 기능 포함
extension KeyedDecodingContainer {
func decodeWrapper<T>(key: K, defaultValue: T) throws -> T
where T : Decodable {
return try decodeIfPresent(T.self, forKey: key) ?? defaultValue
}
}
다음과 같이 init 메서드를 구현할 수 있습니다.
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decodeWrapper(key: .name, defaultValue: "Default Appleseed")
}
하지만 그것은 보다 훨씬 짧지 않다.
self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Default Appleseed"
JSON 키를 찾을 수 없는 경우 원하는 값으로 기본 설정되는 계산된 속성을 사용할 수 있습니다.
class MyCodable: Decodable {
var name: String { return _name ?? "Default Appleseed" }
var age: Int?
// this is the property that gets actually decoded/encoded
private var _name: String?
enum CodingKeys: String, CodingKey {
case _name = "name"
case age
}
}
속성을 읽고 쓸 수 있도록 하려면 세터를 구현할 수도 있습니다.
var name: String {
get { _name ?? "Default Appleseed" }
set { _name = newValue }
}
또 해야 하기 더 장황한 되고, 이 경우 '자세히 말하다'를해야 합니다.CodingKeys
enum하지 않는 ).enum('num'이 됩니다.장점은 커스텀 디코딩/인코딩 코드를 작성할 필요가 없다는 것입니다.이 코드는 어느 시점에서 지루해질 수 있습니다.
이 솔루션은 JSON 키 값이 문자열을 유지하거나 존재하지 않는 경우에만 작동합니다.JSON의 값이 다른 형식(예를 들어 int)에 있는 경우 이 솔루션을 사용해 볼 수 있습니다.
제가 선호하는 접근 방식은 이른바 DTO(데이터 전송 객체)를 사용하는 것입니다.이것은 Codable을 준수하고 원하는 객체를 나타내는 구조입니다.
struct MyClassDTO: Codable {
let items: [String]?
let otherVar: Int?
}
그런 다음 해당 DTO를 사용하여 앱에서 사용하고 싶은 개체를 초기화하기만 하면 됩니다.
class MyClass {
let items: [String]
var otherVar = 3
init(_ dto: MyClassDTO) {
items = dto.items ?? [String]()
otherVar = dto.otherVar ?? 3
}
var dto: MyClassDTO {
return MyClassDTO(items: items, otherVar: otherVar)
}
}
이 접근법은 최종 객체의 이름을 원하는 대로 변경하고 이름을 변경할 수 있기 때문에 좋습니다.이것은 명확하고 수동 디코딩보다 적은 코드를 필요로 합니다.또한 이 방법을 사용하면 네트워킹 계층을 다른 앱과 분리할 수 있습니다.
구현할 수 있습니다.
struct Source : Codable {
let id : String?
let name : String?
enum CodingKeys: String, CodingKey {
case id = "id"
case name = "name"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decodeIfPresent(String.self, forKey: .id) ?? ""
name = try values.decodeIfPresent(String.self, forKey: .name)
}
}
나는 똑같은 것을 찾다가 이 질문을 하게 되었다.여기서의 해결책이 유일한 선택사항이 될까봐 걱정했지만, 제가 찾은 답변은 그다지 만족스럽지 않았습니다.
제 경우 커스텀 디코더를 만들려면 많은 양의 보일러 플레이트가 필요하기 때문에 다른 답을 계속 찾아다녔습니다.
나는 이 기사를 우연히 만났는데, 이 기사는 간단한 경우에 이것을 극복할 수 있는 흥미로운 방법을 보여준다.@propertyWrapper
나에게 가장 중요한 것은 재사용이 가능하고 기존 코드의 리팩터링을 최소화할 필요가 있다는 것이었다.
이 문서에서는 누락된 부울 속성을 실패 없이 기본적으로 false로 설정하려는 경우를 가정하고 있지만 다른 변형도 보여 줍니다.좀 더 자세히 읽어보실 수 있지만, 제 활용 사례를 보여 드리겠습니다.
저 같은 경우에는...array
키가 없어지면 빈칸으로 초기화하고 싶다고 했습니다.
그래서 나는 다음과 같이 선언했다.@propertyWrapper
및 추가 확장:
@propertyWrapper
struct DefaultEmptyArray<T:Codable> {
var wrappedValue: [T] = []
}
//codable extension to encode/decode the wrapped value
extension DefaultEmptyArray: Codable {
func encode(to encoder: Encoder) throws {
try wrappedValue.encode(to: encoder)
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
wrappedValue = try container.decode([T].self)
}
}
extension KeyedDecodingContainer {
func decode<T:Decodable>(_ type: DefaultEmptyArray<T>.Type,
forKey key: Key) throws -> DefaultEmptyArray<T> {
try decodeIfPresent(type, forKey: key) ?? .init()
}
}
이 방법의 장점은 기존 코드의 문제를 쉽게 극복할 수 있다는 것입니다.@propertyWrapper
부동산으로 이동합니다.내 경우:
@DefaultEmptyArray var items: [String] = []
이것이 같은 문제를 다루는 데 도움이 되기를 바랍니다.
갱신:
문제를 계속 조사하면서 이 답변을 올린 후에 나는 이 다른 기사를 찾았지만, 가장 중요한 것은 사용하기 쉬운 공통적인 것을 포함하고 있는 각각의 라이브러리가 있다는 것이다.@propertyWrapper
s는 다음과 같은 경우에 사용됩니다.
https://github.com/marksands/BetterCodable
인코딩 및 디코딩 방식을 구현하지 않으려면 기본값 주위에 다소 더러운 솔루션이 있습니다.
새 필드를 암묵적으로 래핑 해제된 선택사항으로 선언하고 디코딩 후 0인지 확인하고 기본값을 설정할 수 있습니다.
PropertyListEncoder만으로 테스트했는데 JSONDecoder도 같은 방법으로 동작한다고 생각합니다.
자신의 버전을 쓰는 것이init(from decoder: Decoder)
압도적이므로 디코더로 전송하기 전에 입력을 체크하는 방법을 도입하는 것이 좋습니다.이렇게 하면 필드가 없는지 확인하고 사용자 자신의 기본값을 설정할 수 있습니다.
예를 들어 다음과 같습니다.
final class CodableModel: Codable
{
static func customDecode(_ obj: [String: Any]) -> CodableModel?
{
var validatedDict = obj
let someField = validatedDict[CodingKeys.someField.stringValue] ?? false
validatedDict[CodingKeys.someField.stringValue] = someField
guard
let data = try? JSONSerialization.data(withJSONObject: validatedDict, options: .prettyPrinted),
let model = try? CodableModel.decoder.decode(CodableModel.self, from: data) else {
return nil
}
return model
}
//your coding keys, properties, etc.
}
그리고 다음 대신 json에서 개체를 초기화하려면:
do {
let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
let model = try CodableModel.decoder.decode(CodableModel.self, from: data)
} catch {
assertionFailure(error.localizedDescription)
}
Init은 다음과 같습니다.
if let vuvVideoFile = PublicVideoFile.customDecode($0) {
videos.append(vuvVideoFile)
}
이 상황에서는 옵션 처리를 선호합니다만, 다른 의견이 있으면 customDecode(:) 메서드를 슬로우 할 수 있습니다.
언급URL : https://stackoverflow.com/questions/44575293/with-jsondecoder-in-swift-4-can-missing-keys-use-a-default-value-instead-of-hav
'source' 카테고리의 다른 글
스프링 보안: OAuth2 클레임을 역할과 매핑하여 리소스 서버 엔드포인트 보호 (0) | 2023.03.12 |
---|---|
Oracle에서 모든 사용자 테이블/시퀀스 삭제 (0) | 2023.03.12 |
위치 "/"의 일치하는 리프 경로에 요소가 없습니다. (0) | 2023.03.12 |
null이 아닌 스프링 부트 JPA 쿼리 (0) | 2023.03.12 |
웹 팩: ng-include에 대한 요구 사용 (0) | 2023.03.12 |