-
Notifications
You must be signed in to change notification settings - Fork 0
Fix XML builder and parser to handle null and undefined values #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,7 +23,14 @@ export function build(obj: Record<string, unknown>, options?: BuildOptions): str | |
| const parts: string[] = []; | ||
|
|
||
| function serialize(key: string, value: unknown, depth: number): void { | ||
| if (value === null || value === undefined) return; | ||
| if (value === undefined) return; | ||
|
|
||
| if (value === null) { | ||
| const indent = format ? indentBy.repeat(depth) : ''; | ||
| const nl = format ? '\n' : ''; | ||
| parts.push(`${indent}<${key}/>${nl}`); | ||
| return; | ||
| } | ||
|
|
||
| const indent = format ? indentBy.repeat(depth) : ''; | ||
| const nl = format ? '\n' : ''; | ||
|
|
@@ -76,11 +83,7 @@ export function build(obj: Record<string, unknown>, options?: BuildOptions): str | |
| } else { | ||
| // Primitive value | ||
| const str = String(value); | ||
| if (str === '') { | ||
| parts.push(`${indent}<${key}/>${nl}`); | ||
| } else { | ||
| parts.push(`${indent}<${key}>${escapeXml(str)}</${key}>${nl}`); | ||
| } | ||
| parts.push(`${indent}<${key}>${escapeXml(str)}</${key}>${nl}`); | ||
| } | ||
|
Comment on lines
84
to
87
|
||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -260,10 +260,13 @@ function parseAttributes( | |||||||||||||||||||
| const re = /([^\s=]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g; | ||||||||||||||||||||
| let match: RegExpExecArray | null; | ||||||||||||||||||||
| while ((match = re.exec(str)) !== null) { | ||||||||||||||||||||
| let name = match[1]!; | ||||||||||||||||||||
| const rawName = match[1]!; | ||||||||||||||||||||
| let value: string = (match[2] ?? match[3])!; | ||||||||||||||||||||
| if (rmNS) name = removeNS(name); | ||||||||||||||||||||
| // When stripping namespace prefixes, drop xmlns and xmlns:* declarations entirely | ||||||||||||||||||||
| if (rmNS && (rawName === 'xmlns' || rawName.startsWith('xmlns:'))) continue; | ||||||||||||||||||||
|
Comment on lines
+265
to
+266
|
||||||||||||||||||||
| // When stripping namespace prefixes, drop xmlns and xmlns:* declarations entirely | |
| if (rmNS && (rawName === 'xmlns' || rawName.startsWith('xmlns:'))) continue; | |
| // When stripping namespace prefixes, preserve xmlns and xmlns:* declarations | |
| // so callers can still access namespace information if needed. | |
| if (rmNS && (rawName === 'xmlns' || rawName.startsWith('xmlns:'))) { | |
| if (processEnt) value = decodeEntities(value); | |
| attrs[prefix + rawName] = value; | |
| continue; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
serializenow skipsundefinedvalues via an early return, butundefinedcan still be serialized in other paths: (1) object attributes currently doString(v)and will emitattr="undefined", and (2) object child keys withundefinedare still collected intochildEntries, which can force the parent into the "has children" branch and produce<parent></parent>instead of a self-closing tag when all children were skipped. Consider filtering outv === undefinedwhen collectingattrParts/childEntriesso the "skip undefined values" behavior is consistent and doesn’t affect parent rendering.