Skip to content

Commit 6a0e37a

Browse files
fix: resolve toXmlName collision when distinct column names sanitize to the same tag
Column names like '- ' and '_- ' both sanitize to '_-_' via toXmlName, causing a round-trip failure where two input columns produce identical XML element names and readXml collapses them into one column. Build a resolvedTags array before serializing that guarantees unique element names by appending _1, _2, … suffixes to any collisions. Fixes property test: readXml / toXml — property tests > round-trip: toXml then readXml preserves shape Counterexample: [["- ","_- "],1] seed=-849925200 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 182045c commit 6a0e37a

1 file changed

Lines changed: 20 additions & 3 deletions

File tree

src/io/xml.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,23 @@ export function toXml(df: DataFrame, options: ToXmlOptions = {}): string {
501501
const columns = df.columns.toArray();
502502
const nRows = df.shape[0];
503503

504+
// Build unique XML element names, resolving any sanitization collisions.
505+
const assignedTags = new Set<string>();
506+
const resolvedTags = columns.map((col) => {
507+
const base = toXmlName(col);
508+
if (!assignedTags.has(base)) {
509+
assignedTags.add(base);
510+
return base;
511+
}
512+
let i = 1;
513+
while (assignedTags.has(`${base}_${i}`)) {
514+
i++;
515+
}
516+
const unique = `${base}_${i}`;
517+
assignedTags.add(unique);
518+
return unique;
519+
});
520+
504521
for (let i = 0; i < nRows; i++) {
505522
const rowValues: string[] = [];
506523
for (const col of columns) {
@@ -511,16 +528,16 @@ export function toXml(df: DataFrame, options: ToXmlOptions = {}): string {
511528

512529
if (attribs) {
513530
// emit as attributes on the row element
514-
const attrStr = columns
515-
.map((c, j) => `${toXmlName(c)}="${encodeEntities(rowValues[j] ?? "")}"`)
531+
const attrStr = resolvedTags
532+
.map((tag, j) => `${tag}="${encodeEntities(rowValues[j] ?? "")}"`)
516533
.join(" ");
517534
lines.push(`${ind}<${rowName} ${attrStr}/>`);
518535
} else {
519536
// emit as child elements
520537
const childLines: string[] = [];
521538
for (let j = 0; j < columns.length; j++) {
522539
const col = columns[j] ?? "";
523-
const tag = toXmlName(col);
540+
const tag = resolvedTags[j] ?? toXmlName(col);
524541
const raw = rowValues[j] ?? "";
525542
const isCdata = cdataCols.includes(col);
526543
const content = isCdata ? `<![CDATA[${raw}]]>` : encodeEntities(raw);

0 commit comments

Comments
 (0)