v0.x에서 마이그레이션
StAX-XML 1.0은 실험적 reader matrix를 하나의 pure JavaScript token core와 네
public reader 역할로 교체합니다. stax-xml만 설치하면 되며 native, Wasm,
runtime adapter, backend selection package는 없습니다.
마이그레이션 표
섹션 제목: “마이그레이션 표”| v0.x 요구사항 | 1.0 surface |
|---|---|
| XML string을 안정적인 event로 parsing | EventReaderSync |
| String 또는 동기 byte source에서 current token pull | StreamReaderSync |
| 비동기 byte source에서 안정적인 event iteration | EventReader |
| 비동기 byte source에서 current token pull | StreamReader |
| 알려진 XML을 typed object로 projection | stax-xml/converter |
| 메모리에서 XML string 생성 | WriterSync |
| Incremental writing | WriterSyncSink 또는 Writer |
실험적 cursor, batch, adapter, tree/object helper, native, backend API import는
제거하세요. Package entry point는 stax-xml과 stax-xml/converter 두 개뿐이며
root default export는 없습니다.
Public API diff
섹션 제목: “Public API diff”아래 표는 v0.7 public surface와 1.0 대체 API를 symbol 단위로 비교합니다. Stable event object가 필요한지, 재사용되는 current-token view면 충분한지에 따라 reader를 선택하세요.
| v0.x API | 1.0 대체 API | 계약 변경 |
|---|---|---|
StaxXmlParserSync |
EventReaderSync |
동기 iterable은 유지되며 start-element attribute는 record 대신 읽기 전용 Map입니다. |
StaxXmlParserSync |
StreamReaderSync |
Stable event object가 필요 없을 때 선택합니다. next() 후 getter로 current token을 읽습니다. |
StaxXmlParser |
EventReader 또는 StreamReader |
ReadableStream<Uint8Array>와 AsyncIterable<Uint8Array>를 받으며 concurrent next()는 거부합니다. |
StaxXmlCursorReader |
StreamReaderSync |
next()가 boolean 대신 XmlEventType 또는 null을 반환하고 getter의 get prefix가 사라집니다. |
StaxXmlCursorReaderAsync |
StreamReader |
동기 reader와 같은 token/getter 계약을 사용합니다. |
StaxXmlWriterSync / default export |
WriterSync |
Named import를 사용하세요. Package default export는 없습니다. |
StaxXmlWriterSyncSink |
WriterSyncSink |
계속 caller가 SyncTextSink를 제공합니다. |
StaxXmlWriter |
Writer |
WritableStream<Uint8Array> 또는 AsyncTextSink를 제공합니다. |
XmlEventType.ERROR, ErrorEvent, isError |
대체 event 없음 | Parse/decode 실패는 동기 reader에서 throw되고 비동기 read를 reject합니다. |
| v0 대응 없음 | COMMENT, PROCESSING_INSTRUCTION, DTD event |
이제 이 node도 emit됩니다. Exhaustive event switch를 사용하는 consumer는 case를 추가하거나 명시적으로 무시해야 합니다. |
attributes: Record<string, string> / attributesWithPrefix |
attributes: EventAttributes |
qualified name을 key로 쓰는 읽기 전용 Map이며 각 value가 name, localName, prefix, namespaceURI, value를 가집니다. |
Element uri |
namespaceURI |
Prefix와 namespace URI가 없을 때는 빈 string을 사용합니다. |
stax-xml/cursor / platform adapter subpath |
Root reader/writer export | Export되는 package path는 stax-xml과 stax-xml/converter뿐입니다. |
Cursor를 옮길 때는 getAttributeCount()를 attributeCount()로,
getAttributeName()을 attributeName()으로, getAttributeLocalName()을
attributeLocalName()으로, getAttributePrefix()를 attributePrefix()로,
getAttributeValue()를 attributeValue()로, uri()를 namespaceURI()로
바꾸세요. 1.0 reader는 attributeNamespaceURI()와
attributeValue(namespaceURI, localName) namespace-aware lookup도 제공합니다.
Parser option diff
섹션 제목: “Parser option diff”1.0 reader는 더 엄격하지만 v0 entity policy option은 유지합니다. Queue 구현 세부사항을 노출하던 option은 제거됐습니다.
| v0.x parser option | 1.0 동작 또는 대체 방법 |
|---|---|
autoDecodeEntities |
유지, 기본값 true. 활성화하면 predefined entity 5개, 10진수/16진수 numeric reference, configured custom entity를 text와 attribute에서 single-pass decode합니다. false면 원본 reference 표기를 반환합니다. CDATA는 두 mode 모두 literal입니다. |
addEntities |
DTD processing을 원하지 않는 application이 trusted internal entity vocabulary를 제공할 수 있도록 유지됩니다. Text와 attribute에 적용되며 unknown entity는 계속 거부됩니다. 같은 이름의 writer option은 별도 계약입니다. |
eventFilter |
제거. EventReader event를 consumer에서 filter하거나 StreamReader를 사용해 필요 없는 token field를 읽지 마세요. |
maxBufferSize, enableBufferCompaction, initialQueueCapacity |
제거. Buffering, queueing, compaction은 internal 구현 세부사항입니다. |
encoding |
Byte input에 유지. Host TextDecoder에 전달되며 malformed input은 fatal입니다. JavaScript string은 이미 decode된 input입니다. |
| v0 대응 없음 | `documentMode: ‘fragment’ |
autoDecodeEntities: false는 lexical-value mode입니다. Character/attribute value의
reference 표기는 보존하지만 unterminated, unknown, invalid numeric reference는 계속
거부합니다. Namespace processing의 정확성을 위해 namespace binding은 internal decode합니다.
addEntities는 document DTD를 해석하거나 external I/O를 하지 않고 DTD-like application
definition을 제공합니다. Predefined entity 5개를 override할 수 없고 recursive expansion을
하지 않으며 value는 유효한 XML character로 구성되어야 합니다.
다음 예제가 entity 계약을 모두 보여줍니다.
import { EventReaderSync, XmlEventType } from 'stax-xml';
const xml = '<mi title="θ">Θ&#x398;<![CDATA[θ]]></mi>';const addEntities = [{ entity: 'theta', value: 'Θ' }];
for (const event of new EventReaderSync(xml, { addEntities })) { if (event.type === XmlEventType.START_ELEMENT) { console.log(event.attributes.get('title')?.value); // Θ } if (event.type === XmlEventType.CHARACTERS) console.log(event.value); // ΘΘ -- &는 &로 변하지만 결과 text를 다시 decode하지 않음 if (event.type === XmlEventType.CDATA) console.log(event.value); // θ}
// autoDecodeEntities: false이면 text는 "Θ&#x398;",// title attribute는 "θ"로 반환됩니다.Event와 attribute diff
섹션 제목: “Event와 attribute diff”가장 자주 필요한 TypeScript 변경은 attribute access입니다.
// v0.xfor (const event of new StaxXmlParserSync(xml, { autoDecodeEntities: true, eventFilter: { includeAttributes: true, includeCharacters: true, includeCdata: false, },})) { if (event.type === XmlEventType.START_ELEMENT) { console.log(event.uri, event.attributes.title); }}
// 1.0for (const event of new EventReaderSync(xml)) { if (event.type === XmlEventType.CDATA) continue; if (event.type === XmlEventType.START_ELEMENT) { const title = event.attributes.get('title')?.value; console.log(event.namespaceURI, title); }}Materialized event는 qualified name을 O(1)로 조회하고, 순회할 때 source 순서를
유지하며, attribute를 JSON object로 직렬화합니다. __proto__, constructor 같은
JavaScript 예약 key도 일반 Map key로 안전하게 처리합니다. allocation을 최소화하려면
current-token API를 사용하세요.
const reader = new StreamReaderSync(xml);while (reader.next() !== null) { if (reader.eventType() === XmlEventType.START_ELEMENT) { console.log(reader.namespaceURI(), reader.attributeValue('title')); }}EventReaderSync/EventReader가 반환하는 stable object는 보관해도 됩니다.
StreamReaderSync/StreamReader는 current token만 노출하므로 다음 next()를
호출하기 전에 getter value를 소비하세요.
Writer diff
섹션 제목: “Writer diff”Write method, getXmlString(), mutable formatting method는 v0 이름을 유지합니다.
Async writer의 구현 세부 진단 API인 getMetrics()는 제거됐습니다. 그 밖의 주요
call-site 변경은 class 이름, named import, namespace 설정, output-encoding
boundary입니다.
v0 namespaces constructor option도 제거됐습니다. Namespace scope가 시작되는
element를 연 뒤 character나 child를 쓰기 전에 namespace를 선언하세요.
import { WriterSync } from 'stax-xml';
const writer = new WriterSync({ prettyPrint: true, indentString: ' ' });writer.writeStartElement('feed');writer.writeNamespace('', 'urn:example:feed');writer.writeNamespace('media', 'urn:example:media');writer.writeEndElement();writer.writeEndDocument();
const xml = writer.getXmlString();Writer byte-stream output은 UTF-8이고 WriterSync declaration metadata도
UTF-8로 제한됩니다. 다른 encoding이 필요하면 encoding field가 있는
AsyncTextSink 또는 SyncTextSink를 전달하고 실제 encoding을 sink에서 수행하세요.
메모리의 String
섹션 제목: “메모리의 String”동기 reader는 string을 직접 받으며 먼저 Uint8Array로 encoding하지 않습니다.
import { EventReaderSync, XmlEventType } from 'stax-xml';
for (const event of new EventReaderSync('<root><item/></root>')) { if (event.type === XmlEventType.START_ELEMENT) console.log(event.name);}Event object 할당을 줄이는 것이 중요하면 StreamReaderSync를 사용합니다.
import { StreamReaderSync, XmlEventType } from 'stax-xml';
const reader = new StreamReaderSync('<root><item id="1"/></root>');while (reader.next() !== null) { if (reader.eventType() === XmlEventType.START_ELEMENT) { console.log(reader.name(), reader.attributeValue('id')); }}같은 reader는 Uint8Array 또는 Iterable<Uint8Array>도 받습니다.
Byte reader의 encoding option에는 host TextDecoder가 지원하는 label을 지정할 수
있습니다. 기본값은 utf-8이며 decode 오류는 즉시 throw됩니다. documentMode의
기본값은 fragment이고, root element가 정확히 하나여야 한다면 document를
선택합니다. Namespace 처리는 기본 활성화되며 raw qualified name만 필요하면
namespaceAware: false로 끌 수 있습니다. Parser와 writer의 계약은 XML 1.0이므로
XML 1.1 declaration은 거부됩니다.
Node.js Stream
섹션 제목: “Node.js Stream”비동기 reader는 web ReadableStream<Uint8Array> 또는
AsyncIterable<Uint8Array>를 받습니다. Node.js Readable의 Buffer chunk는
Uint8Array이므로 stream을 직접 전달할 수 있습니다.
import { createReadStream } from 'node:fs';import { EventReader, XmlEventType } from 'stax-xml';
export async function countElements(path: string) { let count = 0; for await (const event of new EventReader(createReadStream(path))) { if (event.type === XmlEventType.START_ELEMENT) count++; } return count;}for await를 일찍 끝내면 event reader가 source를 반환합니다. Manual pull loop를
일찍 끝낼 때는 StreamReader.close()를 호출하세요.
Converter
섹션 제목: “Converter”Schema는 compiled dispatch plan을 자동으로 재사용하므로 public .compile() 단계가
없습니다.
import { x } from 'stax-xml/converter';
const feed = x.object({ title: x.string('/rss/channel/title'), items: x.array(x.string('./title'), '/rss/channel/item'),});
const value = feed.parseSync(xmlString);Converter는 streaming 방식이며 DOM parser를 도입하지 않습니다. XML shape를 미리 알 수 없다면 네 reader API를 사용하세요.
CommonJS
섹션 제목: “CommonJS”Package는 ESM-only입니다. CommonJS에서는 dynamic import를 사용할 수 있습니다.
import('stax-xml').then(({ EventReaderSync }) => { for (const event of new EventReaderSync('<root/>')) console.log(event.type);});