이 도구는 샘플 XML에 대한 Swift Codable 구조체를 씁니다. 모든 XML 변환기가 공유하는 규칙으로 XML을 데이터로 매핑하고, 속성이나 자식이 있는 요소마다 구조체가 하나 생깁니다. 프로퍼티는 기본적으로 let이며 스위프트 이름을 쓰므로 키 @data-id는 프로퍼티 dataId가 됩니다.
프로퍼티 이름이 키와 다르면 구조체에 모든 프로퍼티를 나열한 enum CodingKeys: String, CodingKey가 생기고 case sku = "@sku"처럼 원래 철자가 들어갑니다. 그러면 JSONDecoder가 키 전략 없이도 변환된 JSON을 디코딩합니다. 원본 XML을 읽는 구조체는 아니며, 그러려면 XMLParser나 XML 코더 라이브러리가 필요합니다.
입력은 엄격한 XML 1.0 파서가 읽고 오류에는 정확한 행과 열을 알려 줍니다. 20 MiB까지 페이지 안에서 처리하며 업로드하지 않습니다.
사용 방법
XML 붙여넣기
문서를 붙여 넣거나 예제를 고릅니다. 입력할 때마다 구조체가 갱신됩니다.
매핑 설정
XML 데이터 매핑 패널에서 속성 접두사, 텍스트 키, 항상 배열, 숫자 추론을 고릅니다.
Swift 옵션 선택
루트 타입 이름(기본
Payload)과,let대신var를 쓸지를 정합니다.CodingKeys 검토
키가 프로퍼티 이름과 다른 구조체에만 CodingKeys가 생깁니다. 모든 프로퍼티가 나열됐는지 확인하세요.
복사 또는 다운로드
코드를 복사하거나 루트 타입 이름의
Payload.swift로 내려받습니다.
작동 원리
타입과 옵셔널
여기서 스위프트는 정수형과 부동소수점형을 하나씩만 쓰므로 정수는 모두 Int, 소수는 모두 Double입니다. 일부 발생에 없는 키는 ?가 붙은 옵셔널이 되고, Codable이 decodeIfPresent를 자동으로 만들어 줍니다.
| XML | Swift |
|---|---|
<name>Ada</name> | let name: String |
id="7" | let id: Int, case id = "@id" |
<price>19.5</price> | let price: Double |
<ok>true</ok> | let ok: Bool |
반복되는 <item> | let item: [PayloadItem] |
| 일부 item에만 있는 키 | let qty: Int? |
<v>1</v><v>a</v> | [JSONValue]와 JSONValue 열거형 |
CodingKeys와 JSONValue 보조 타입
스위프트 식별자에는 @, #, :, -를 쓸 수 없으므로 이런 키는 lowerCamelCase로 바뀌고 원래 키는 CodingKeys에 남습니다. CodingKeys를 선언하면 모든 프로퍼티를 나열해야 하므로 case customer처럼 이름 변경이 필요 없는 항목도 함께 들어갑니다.
한 위치에 종류가 섞인 값이 있으면 문자열, 숫자, 불리언, 객체, 배열, null을 다루는 indirect enum JSONValue: Codable이 파일 끝에 한 번 추가됩니다.
struct PayloadOrderItem: Codable {
let sku: String
let qty: Int?
let text: String
enum CodingKeys: String, CodingKey {
case sku = "@sku"
case qty = "@qty"
case text = "#text"
}
}단일 자식과 배열
<item>이 하나면 중첩 구조체, 둘이면 배열이 됩니다. 항상 배열 스위치를 켜면 샘플에 하나뿐이어도 [PayloadItem]이 되어 긴 문서에서 JSONDecoder가 typeMismatch를 던지지 않습니다.
실제 예시
<order id="1042"><item sku="A1" qty="1">Pen</item><item sku="B7">Ink</item></order>import Foundation
struct Payload: Codable {
let order: PayloadOrder
}
struct PayloadOrder: Codable {
let id: Int
let item: [PayloadOrderItem]
enum CodingKeys: String, CodingKey {
case id = "@id"
case item
}
}
struct PayloadOrderItem: Codable {
let sku: String
let qty: Int?
let text: String
enum CodingKeys: String, CodingKey {
case sku = "@sku"
case qty = "@qty"
case text = "#text"
}
}키 이름이 바뀐 구조체마다 모든 프로퍼티를 나열한 CodingKeys가 생깁니다. qty는 첫 번째 item에만 있어 Int?입니다.
<doc xml:lang="en" data-id="5"><title>Hi</title></doc>import Foundation
struct Payload: Codable {
let doc: PayloadDoc
}
struct PayloadDoc: Codable {
let xmlLang: String
let dataId: Int
let title: String
enum CodingKeys: String, CodingKey {
case xmlLang = "@xml:lang"
case dataId = "@data-id"
case title
}
}@xml:lang과 @data-id는 xmlLang, dataId가 되고, 키와 이름이 같은 title은 이름만 있는 case로 충분합니다.
<r><v>1</v><v>a</v></r>import Foundation
struct Payload: Codable {
let r: PayloadR
}
struct PayloadR: Codable {
let v: [JSONValue]
}
indirect enum JSONValue: Codable {
case string(String)
case number(Double)
case bool(Bool)
case object([String: JSONValue])
case array([JSONValue])
case null
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if container.decodeNil() { self = .null; return }
if let value = try? container.decode(Bool.self) { self = .bool(value); return }
if let value = try? container.decode(Double.self) { self = .number(value); return }
if let value = try? container.decode(String.self) { self = .string(value); return }
if let value = try? container.decode([String: JSONValue].self) { self = .object(value); return }
if let value = try? container.decode([JSONValue].self) { self = .array(value); return }
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unsupported JSON value")
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .string(let value): try container.encode(value)
case .number(let value): try container.encode(value)
case .bool(let value): try container.encode(value)
case .object(let value): try container.encode(value)
case .array(let value): try container.encode(value)
case .null: try container.encodeNil()
}
}
}같은 요소 이름에 숫자와 단어가 있으면 하나의 스위프트 타입으로 쓸 수 없어 v는 [JSONValue]가 되고 보조 열거형이 붙습니다.
자주 하는 실수
CodingKeys 없이는 이름이 맞지 않습니다
프로퍼티 이름을 직접 바꿨다면
case줄도 함께 고치세요.id라는 프로퍼티로는 키@id를 디코딩할 수 없습니다.Int는 64비트 플랫폼에서만 64비트입니다
2^53보다 큰 ID는 추론 규칙에 따라
String으로 남지만, 그 아래의 큰 숫자는 32비트 대상에서Int범위를 넘을 수 있습니다.정수처럼 보이는 소수
19.0은 숫자 19로 읽혀 프로퍼티가Int가 되고, 나중에19.5가 오면 디코딩에 실패합니다. 추론을 끄거나 타입을Double로 고치세요.XMLParser용 모델이 아닙니다
이 구조체는 JSON을 디코딩합니다.
XMLParser나 XML 코더는@sku같은 키가 아니라 요소 이름과 속성을 찾습니다.
자주 묻는 질문
모든 구조체에 CodingKeys가 생기나요?
키가 스위프트 프로퍼티 이름과 다른 항목이 하나라도 있는 구조체에만 생깁니다. 키가 모두 같다면 자동 합성되는 키를 씁니다.
프로퍼티가 왜 옵셔널인가요?
그 요소의 일부 발생에 해당 키가 없었기 때문입니다. 옵셔널이면 JSONDecoder가 예외 대신 nil로 둡니다.
변경 가능 옵션은 무엇을 바꾸나요?
모든 프로퍼티를 let 대신 var로 써서 디코딩한 뒤에도 값을 고칠 수 있게 합니다.
keyDecodingStrategy와 같이 써도 되나요?
필요 없습니다. CodingKeys에 정확한 키가 이미 있어서 convertFromSnakeCase 같은 전략은 오히려 방해가 됩니다.
리스트는 어떤 타입이 되나요?
합쳐진 구조체의 배열이며 예를 들어 [PayloadItem], 텍스트만 있는 요소는 [String]입니다.
XML이 업로드되나요?
아니요. 모든 처리가 브라우저에서 이뤄지고 문서는 기기에 남습니다.
마지막 업데이트:
