Skip to content

fix: preserve negative zero sign when stringifying values - #22

Merged
amitguptagwl merged 1 commit into
NaturalIntelligence:mainfrom
rajanpanth:fix/negative-zero-stringify
Aug 14, 2026
Merged

fix: preserve negative zero sign when stringifying values#22
amitguptagwl merged 1 commit into
NaturalIntelligence:mainfrom
rajanpanth:fix/negative-zero-stringify

Conversation

@rajanpanth

Copy link
Copy Markdown
Contributor

Summary

Every raw-value-to-string conversion in the builder silently drops the sign of -0, corrupting it to 0:

const builder = new XMLBuilder();
builder.build({ a: -0 }); // '<a>0</a>' -- should be '<a>-0</a>'

This affects text values, attribute values, oneListGroup array items, and stopNode raw content — in both preserveOrder modes. XML has no separate int/float syntax, so the sign carries the only signal that a value was a negative float rather than 0, and it's silently lost on a build round trip (e.g. parser.parse(builder.build(x)) no longer matches x for any -0 value).

Root cause: String(val), val.toString(), and '' + val all render JS -0 as "0" — none of them special-case it — and the builder does this raw-to-string conversion at many separate call sites across fxb.js, orderedJs2Xml.js, and util.js.

Changes

  • src/util.js: add a small shared valToStr() helper that special-cases Object.is(val, -0), and use it inside safeComment/safeCdata/escapeAttribute instead of their bare String(val).
  • src/fxb.js / src/orderedJs2Xml.js: replace every ad hoc '' + val / val.toString() / implicit-ToString raw-value stringification with valToStr(), covering: attribute values, text values, oneListGroup array items, and stopNode raw content, in both preserveOrder: false and preserveOrder: true.
  • Tests added alongside each affected code path in spec/j2x_spec.js, spec/j2x_ordered_spec.js, and spec/stopNodes_spec.js (7 new cases total).

Testing

  • Full suite: 136/136 passing (129 pre-existing + 7 new), 1 pre-existing pending spec unrelated to this change.
  • Manually verified every affected path before/after (text, attribute, oneListGroup, stopNode, CDATA, comment — both preserveOrder modes) using a throwaway script, then encoded the cases as committed tests.
  • npm run lint reports the same pre-existing errors with and without this change (unrelated files/lines; confirmed via git stash) — this PR introduces no new lint issues.

Every raw-value-to-string conversion in the builder (String()/toString()/
implicit ToString via concatenation) silently drops the sign of -0,
corrupting it to 0 for both text and attribute values, in both
preserveOrder modes, and in stopNode raw-content paths. XML has no
separate int/float syntax, so this loss is silent and easy to miss on
a parse -> build round trip.

Add a single shared valToStr() helper that special-cases Object.is(val,
-0) and use it at every such conversion site instead of the ad hoc
String(val)/''+val/val.toString() calls.
@rajanpanth

Copy link
Copy Markdown
Contributor Author

Friendly follow-up on this PR 👋

When you have a moment, could you please take a look? I’m happy to update anything if needed.

Thanks!

@amitguptagwl

Copy link
Copy Markdown
Member

Thanks @rajanpanth for this PR. The only issue I see with this is that it may break the experience of existing users.So if I should release it as minor version or not. let me think

@amitguptagwl
amitguptagwl merged commit e9e8db6 into NaturalIntelligence:main Aug 14, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants