Swift 4의 JSONDecoder를 사용하면 키가 누락된 경우 옵션 속성 대신 기본값을 사용할 수 있습니까?
스위프트 4를 했다.Codable프로토콜입니다.★★★★★★★를 사용하는 JSONDecoder이 아닌 로 하는 것 .CodableJSON을 사용하다
제가 정말로 원하는 것은 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 }
}
또 해야 하기 더 장황한 되고, 이 경우 '자세히 말하다'를해야 합니다.CodingKeysenum하지 않는 ).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] = []
이것이 같은 문제를 다루는 데 도움이 되기를 바랍니다.
갱신:
문제를 계속 조사하면서 이 답변을 올린 후에 나는 이 다른 기사를 찾았지만, 가장 중요한 것은 사용하기 쉬운 공통적인 것을 포함하고 있는 각각의 라이브러리가 있다는 것이다.@propertyWrappers는 다음과 같은 경우에 사용됩니다.
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 |