이 변환기는 XML 문서에 대한 Dart 클래스를 만듭니다. 공유 규칙으로 XML을 데이터로 매핑한 뒤, 속성이나 자식이 있는 요소마다 클래스를 하나씩 씁니다. 각 클래스에는 final 필드, required 이름 있는 매개변수 생성자, factory fromJson(Map<String, dynamic> json), toJson()이 들어갑니다.
fromJson의 맵 키는 속성 접두사를 포함한 데이터 키 그대로여서 json['@sku'] as String이 속성 sku를 읽습니다. 필드 이름은 lowerCamelCase이고 Dart 예약어는 피합니다. 코드는 json_serializable이나 build_runner 없는 순수 Dart이며 원본 XML이 아니라 변환된 JSON을 받습니다. 문서 자체를 읽으려면 xml 같은 패키지를 쓰세요.
입력은 엄격한 XML 1.0 파서가 읽고 오류에는 행과 열이 표시되며 20 MiB까지 받습니다. 문서는 브라우저를 떠나지 않습니다.
사용 방법
XML 붙여넣기
문서를 붙여 넣거나 예제를 불러옵니다. 클래스가 실시간으로 다시 생성됩니다.
매핑 설정
XML 데이터 매핑 패널에서 속성 접두사, 텍스트 키, 항상 배열, 숫자 추론을 고릅니다.
루트 이름 정하기
루트 클래스 이름(기본
Payload)을 입력합니다. 파일 이름은 스네이크 케이스인payload.dart가 됩니다.클래스 읽기
각 클래스에 필드, 생성자,
fromJson,toJson이 나옵니다. 선택 필드는 nullable이며 null이 아닐 때만 출력됩니다.복사 또는 다운로드
코드를 복사하거나
.dart파일로 내려받아lib/models에 넣으세요.
작동 원리
타입과 fromJson의 캐스트
Dart에는 int와 double이 있어 크기와 상관없이 정수는 int, 소수는 double입니다. 모든 값을 명시적 캐스트로 읽으므로 중첩된 리스트도 실행 중 예기치 않게 실패하지 않습니다.
| XML | Dart 필드 | fromJson 식 |
|---|---|---|
<name>Ada</name> | final String name | json['name'] as String |
id="7" | final int id | json['@id'] as int |
<price>19.5</price> | final double price | (json['price'] as num).toDouble() |
<ok>true</ok> | final bool ok | json['ok'] as bool |
반복되는 <item> | final List<PayloadItem> item | List<dynamic>에 .map(...).toList() |
| 일부 item에만 있는 키 | final int? qty | json['@qty'] as int? |
<v>1</v><v>a</v> | final List<dynamic> v | json['v'] as List<dynamic> |
선택 필드와 toJson
일부 발생에서 빠진 키는 nullable이고 생성자에서 required가 아니며, toJson에서는 if (qty != null) '@qty': qty로 써서 왕복 변환 때 없던 키가 생기지 않습니다. 소수는 num을 거쳐 읽으므로, 샘플이 19.5였는데 값이 19로 오는 경우에도 타입 오류가 나지 않습니다.
반복 요소의 모든 발생은 클래스 하나를 공유합니다. 같은 요소 이름에 종류가 다른 값이 있으면 필드는 dynamic이 됩니다.
factory PayloadOrderItem.fromJson(Map<String, dynamic> json) => PayloadOrderItem(
sku: json['@sku'] as String,
qty: json['@qty'] as int?,
text: json['#text'] as String,
);자식 하나일 때와 리스트
<item>이 하나면 중첩 객체로, 여럿이면 리스트로 읽습니다. 자식 요소는 항상 배열로 옵션을 켜면 샘플에 하나뿐이어도 List<dynamic>으로 읽어, 더 긴 문서에서 TypeError가 나지 않습니다.
실제 예시
<order id="1042"><item sku="A1" qty="1">Pen</item><item sku="B7">Ink</item></order>class Payload {
final PayloadOrder order;
Payload({
required this.order,
});
factory Payload.fromJson(Map<String, dynamic> json) => Payload(
order: PayloadOrder.fromJson(json['order'] as Map<String, dynamic>),
);
Map<String, dynamic> toJson() => {
'order': order.toJson(),
};
}
class PayloadOrder {
final int id;
final List<PayloadOrderItem> item;
PayloadOrder({
required this.id,
required this.item,
});
factory PayloadOrder.fromJson(Map<String, dynamic> json) => PayloadOrder(
id: json['@id'] as int,
item: (json['item'] as List<dynamic>).map((e0) => PayloadOrderItem.fromJson(e0 as Map<String, dynamic>)).toList(),
);
Map<String, dynamic> toJson() => {
'@id': id,
'item': item.map((e0) => e0.toJson()).toList(),
};
}
class PayloadOrderItem {
final String sku;
final int? qty;
final String text;
PayloadOrderItem({
required this.sku,
this.qty,
required this.text,
});
factory PayloadOrderItem.fromJson(Map<String, dynamic> json) => PayloadOrderItem(
sku: json['@sku'] as String,
qty: json['@qty'] as int?,
text: json['#text'] as String,
);
Map<String, dynamic> toJson() => {
'@sku': sku,
if (qty != null) '@qty': qty,
'#text': text,
};
}item 리스트는 map과 toList로 변환됩니다. qty는 int?이고 null이면 toJson에서 빠지며 키에는 @ 접두사가 유지됩니다.
<doc xml:lang="en" data-id="5"><title>Hi</title></doc>class Payload {
final PayloadDoc doc;
Payload({
required this.doc,
});
factory Payload.fromJson(Map<String, dynamic> json) => Payload(
doc: PayloadDoc.fromJson(json['doc'] as Map<String, dynamic>),
);
Map<String, dynamic> toJson() => {
'doc': doc.toJson(),
};
}
class PayloadDoc {
final String xmlLang;
final int dataId;
final String title;
PayloadDoc({
required this.xmlLang,
required this.dataId,
required this.title,
});
factory PayloadDoc.fromJson(Map<String, dynamic> json) => PayloadDoc(
xmlLang: json['@xml:lang'] as String,
dataId: json['@data-id'] as int,
title: json['title'] as String,
);
Map<String, dynamic> toJson() => {
'@xml:lang': xmlLang,
'@data-id': dataId,
'title': title,
};
}필드는 xmlLang, dataId이고 맵 키는 '@xml:lang', '@data-id'로 남습니다.
<r><v>1</v><v>a</v></r>class Payload {
final PayloadR r;
Payload({
required this.r,
});
factory Payload.fromJson(Map<String, dynamic> json) => Payload(
r: PayloadR.fromJson(json['r'] as Map<String, dynamic>),
);
Map<String, dynamic> toJson() => {
'r': r.toJson(),
};
}
class PayloadR {
final List<dynamic> v;
PayloadR({
required this.v,
});
factory PayloadR.fromJson(Map<String, dynamic> json) => PayloadR(
v: json['v'] as List<dynamic>,
);
Map<String, dynamic> toJson() => {
'v': v,
};
}같은 요소 이름에 숫자와 단어가 있으면 타입을 하나로 정할 수 없어 v는 List<dynamic>이 되고 그대로 전달됩니다.
자주 하는 실수
xml 패키지용이 아닙니다
fromJson은@id같은 키가 있는 맵을 기대합니다.xml패키지는 요소와 속성을 파싱하므로 먼저 변환된 JSON으로 바꿔야 합니다.정수처럼 보이는 소수
19.0은 19로 읽혀 필드가int가 되고, 나중에19.5가 오면as int캐스트에서TypeError가 납니다. 추론을 끄거나 타입을double로 바꾸세요.맵 키의 @
json['@id']는 올바른 Dart이지만 오타가 나기 쉽습니다. 접두사가 싫다면 매핑 패널에서 비우세요. 그러면 키가id가 됩니다.뒤쪽 문서에서만 나타나는 필드
선택 키는 붙여 넣은 샘플의 발생끼리 비교해 찾습니다. 샘플에 없는 키는 필드가 없어
fromJson이 무시합니다.
자주 묻는 질문
json_serializable이 필요한가요?
아니요. 직접 작성한 듯한 fromJson과 toJson이 들어 있어 빌드 단계가 없습니다.
필드 이름이 왜 xmlLang인가요?
@xml:lang은 Dart 식별자가 될 수 없어 필드는 lowerCamelCase로 만들고, 맵 키에는 정확한 철자를 남깁니다.
toJson은 선택 필드를 어떻게 처리하나요?
null이 아닐 때만 써서, 직렬화해도 입력에 없던 키가 추가되지 않습니다.
Flutter에서 쓸 수 있나요?
네. 핵심 Dart 타입과 Map<String, dynamic>, List만 쓰므로 jsonDecode 결과에 그대로 쓸 수 있습니다.
여기서 dynamic은 무슨 뜻인가요?
숫자와 단어처럼 종류가 다른 값이 섞여서 더 구체적인 타입을 정할 수 없다는 뜻입니다.
XML이 서버로 전송되나요?
아니요. 파싱과 코드 생성이 브라우저에서 실행됩니다.
마지막 업데이트:
