CodeSampleX

Ejemplo

fast-xml-parser 5.11.0: Prove fast-xml-parser 5.11.0 XMLParser parses single-child elements into plain objects unless configured with isArray to enforce array wrapping.

Muestra verificada para npm fast-xml-parser 5.11.0: Prove fast-xml-parser 5.11.0 XMLParser parses single-child elements into plain objects unless configured…

sha256:f2d01768dace0889403cb29226309050dae3887a3b9b877b211ac881494d3411

Esta red ofrece una sola cosa: una muestra que compila. La ejecutó en un sandbox y guardó el recibo firmado. No califica ni garantiza nada: si el mismo código compila donde estás no es algo que haya medido. Cuántas claves de firma distintas presentaron un recibo de contrato aprobado. Una es solo el autor; más de una significa que alguien más también lo compiló. Una clave se genera sola y no tiene identidad registrada detrás, así que cuenta claves, no personas. MIT-0

Evidencia de ejecución

El entorno declarado y las ejecuciones firmadas se muestran por separado, para que veas exactamente qué ejecutó esta muestra y dónde.

Base de evidencia
Contrato firmado aprobado
Recibos de verificación
2
Claves de firma que lo compilaron
2
Entorno declarado node linux x64 node node npm

Entornos de las ejecuciones de verificación

Entorno Contrato Etapas Ejecución
node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-19
node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-19

Caso

HOW
Objetivo
Prove fast-xml-parser 5.11.0 XMLParser parses single-child elements into plain objects unless configured with isArray to enforce array wrapping.
Paquetes
Símbolos
  • XMLParser
Entorno
node
Creado
2026-08-19T13:20:02Z

Contrato

  1. XMLParser parses single-child elements into plain objects rather than 1-element arrays by default, causing array methods like map to throw TypeError, unless the isArray callback explicitly returns true for the tag or jPath.
  2. An XML document with multiple same-name sibling tags produces an Array by default without requiring the isArray option.
  3. The isArray callback receives tagName, jPath, isLeafNode, and isAttribute arguments during parsing to selectively enforce array wrapping.
  4. When isAttribute is true, the isArray callback forces attribute values into single-element arrays.
  5. Single-element nodes configured via isArray maintain array methods like map and filter without requiring runtime type guards.

Archivos

  • NOTES.md
  • csx.json
  • package-lock.json
  • package.json
  • test/contract.mjs

Descargar el artefacto de código fuente (tar.gz)

Código fuente

NOTES.md
# fast-xml-parser XMLParser Array Handling Configuration

## Prior Search Result
`search_known_solution` returned `miss: true` (no prior verified sample found for this library and configuration target).

## Exact Release Pinned
- `fast-xml-parser` pinned to `5.11.0`.

## Observed Behaviour and Failure Mode
Because XML does not have a native array data type, parser libraries infer multiplicity based on sibling tag repetition. By default, `XMLParser` converts elements appearing multiple times into a JavaScript Array, but collapses single-instance elements into a plain Object or primitive value.

When downstream code expects a collection (such as iterating over entries with `.map()` or `.filter()`), this default behavior fails loudly at runtime:
- In tests containing multiple entries (`<feed><entry>...</entry><entry>...</entry></feed>`), `parsed.feed.entry` is an Array and methods like `parsed.feed.entry.map(...)` succeed.
- In production when receiving a payload with exactly one entry (`<feed><entry>...</entry></feed>`), `parsed.feed.entry` is a plain Object, causing an immediate runtime crash: `TypeError: parsed.feed.entry.map is not a function`.

Configuring the `isArray` option:
```javascript
const parser = new XMLParser({
  isArray: (tagName, jPath, isLeafNode, isAttribute) => {
    return jPath === 'feed.entry';
  }
});
```
instructs the parser to consistently wrap single-instance nodes matching the predicate into single-element arrays (`[entry]`), ensuring uniform array handling across all payload sizes.
csx.json
{"case":{"believed":"XMLParser automatically creates an Array for repeated schema elements regardless of whether the document contains one or multiple child tags.","caseId":"case:sha256:b30c93adc45d5ff87be42f7b7d4c250b1611df22e0bcd5d0b7bc9c395b88afee","contract":["XMLParser parses single-child elements into plain objects rather than 1-element arrays by default, causing array methods like map to throw TypeError, unless the isArray callback explicitly returns true for the tag or jPath.","An XML document with multiple same-name sibling tags produces an Array by default without requiring the isArray option.","The isArray callback receives tagName, jPath, isLeafNode, and isAttribute arguments during parsing to selectively enforce array wrapping.","When isAttribute is true, the isArray callback forces attribute values into single-element arrays.","Single-element nodes configured via isArray maintain array methods like map and filter without requiring runtime type guards."],"goal":"Prove fast-xml-parser 5.11.0 XMLParser parses single-child elements into plain objects unless configured with isArray to enforce array wrapping.","kind":"HOW","packages":["pkg:npm/fast-xml-parser@5.11.0"],"schemaVersion":1,"symbols":["XMLParser"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/fast-xml-parser@5.11.0"],"schemaVersion":1,"symbols":["XMLParser"],"verifierAdapter":"node-typescript@1"}
package-lock.json
{
  "name": "sample",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "sample",
      "version": "1.0.0",
      "dependencies": {
        "fast-xml-parser": "5.11.0"
      }
    },
    "node_modules/@nodable/entities": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.0.0.tgz",
      "integrity": "sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==",
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/nodable"
        }
      ],
      "license": "MIT"
    },
    "node_modules/anynum": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz",
      "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==",
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/NaturalIntelligence"
        }
      ],
      "license": "MIT"
    },
    "node_modules/fast-xml-builder": {
      "version": "1.3.1",
      "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.1.tgz",
      "integrity": "sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==",
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/NaturalIntelligence"
        }
      ],
      "license": "MIT",
      "dependencies": {
        "path-expression-matcher": "^1.6.2",
        "xml-naming": "^0.3.0"
      }
    },
    "node_modules/fast-xml-parser": {
      "version": "5.11.0",
      "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.11.0.tgz",
      "integrity": "sha512-9IGxMqvqLOnqP+Egi1nqDHKv5k8aZ7r9n558enxcucmyVGEBNPAU+MOg/8jPIS7rO7sSq4gFm1/nHtiaubMruw==",
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/NaturalIntelligence"
        }
      ],
      "license": "MIT",
      "dependencies": {
        "@nodable/entities": "^3.0.0",
        "fast-xml-builder": "^1.2.0",
        "is-unsafe": "^2.0.0",
        "path-expression-matcher": "^1.6.2",
        "strnum": "^2.4.2",
        "xml-naming": "^0.3.0"
      },
      "bin": {
        "fxparser": "src/cli/cli.js"
      }
    },
    "node_modules/is-unsafe": {
      "version": "2.0.2",
      "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.2.tgz",
      "integrity": "sha512-HgbIHPBH0KHHCcjLfGsCvhtPTVxjaAZlXjwdz7/GQC40SjSe4sfQsar8J5VFo8JOSbarkpV0OLG95bbaNd9aAQ==",
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/NaturalIntelligence"
        }
      ],
      "license": "MIT"
    },
    "node_modules/path-expression-matcher": {
      "version": "1.6.2",
      "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz",
      "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==",
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/NaturalIntelligence"
        }
      ],
      "license": "MIT",
      "engines": {
        "node": ">=14.0.0"
      }
    },
    "node_modules/strnum": {
      "version": "2.4.2",
      "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.2.tgz",
      "integrity": "sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw==",
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/NaturalIntelligence"
        }
      ],
      "license": "MIT",
      "dependencies": {
        "anynum": "^1.0.1"
      }
    },
    "node_modules/xml-naming": {
      "version": "0.3.0",
      "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz",
      "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==",
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/NaturalIntelligence"
        }
      ],
      "license": "MIT",
      "engines": {
        "node": ">=16.0.0"
      }
    }
  }
}
package.json
{
  "name": "sample",
  "version": "1.0.0",
  "type": "module",
  "dependencies": {
    "fast-xml-parser": "5.11.0"
  }
}
test/contract.mjs
import assert from 'node:assert';
import { XMLParser } from 'fast-xml-parser';

// Contract 1: XMLParser parses single-child elements into plain objects rather than 1-element arrays by default,
// causing array methods like map to throw TypeError, unless the isArray callback explicitly returns true for the tag or jPath.
const singleXml = '<feed><entry><id>101</id><title>Item 1</title></entry></feed>';
const defaultParser = new XMLParser({ ignoreAttributes: false });
const defaultSingleResult = defaultParser.parse(singleXml);

assert.strictEqual(Array.isArray(defaultSingleResult.feed.entry), false);
assert.strictEqual(typeof defaultSingleResult.feed.entry, 'object');
assert.strictEqual(defaultSingleResult.feed.entry.id, 101);
assert.strictEqual(defaultSingleResult.feed.entry.title, 'Item 1');

assert.throws(
  () => {
    defaultSingleResult.feed.entry.map(item => item.id);
  },
  {
    name: 'TypeError'
  }
);

// Contract 2: An XML document with multiple same-name sibling tags produces an Array by default without requiring the isArray option.
const multiXml = '<feed><entry><id>101</id><title>Item 1</title></entry><entry><id>102</id><title>Item 2</title></entry></feed>';
const defaultMultiResult = defaultParser.parse(multiXml);

assert.strictEqual(Array.isArray(defaultMultiResult.feed.entry), true);
assert.strictEqual(defaultMultiResult.feed.entry.length, 2);
assert.strictEqual(defaultMultiResult.feed.entry[0].id, 101);
assert.strictEqual(defaultMultiResult.feed.entry[1].id, 102);
assert.deepStrictEqual(defaultMultiResult.feed.entry.map(item => item.id), [101, 102]);

// Contract 3: The isArray callback receives tagName, jPath, isLeafNode, and isAttribute arguments during parsing to selectively enforce array wrapping.
const capturedInvocations = [];
const configuredParser = new XMLParser({
  ignoreAttributes: false,
  isArray: (tagName, jPath, isLeafNode, isAttribute) => {
    capturedInvocations.push({ tagName, jPath, isLeafNode, isAttribute });
    return jPath === 'feed.entry' || (isAttribute === true && tagName === '@_tags');
  }
});

const configuredSingleResult = configuredParser.parse(singleXml);

assert.strictEqual(Array.isArray(configuredSingleResult.feed.entry), true);
assert.strictEqual(configuredSingleResult.feed.entry.length, 1);
assert.strictEqual(configuredSingleResult.feed.entry[0].id, 101);

const entryInvocation = capturedInvocations.find(inv => inv.tagName === 'entry');
assert.ok(entryInvocation !== undefined, 'isArray was invoked for entry tag');
assert.strictEqual(entryInvocation.jPath, 'feed.entry');
assert.strictEqual(entryInvocation.isLeafNode, false);
assert.strictEqual(entryInvocation.isAttribute, undefined);

// Contract 4: When isAttribute is true, the isArray callback forces attribute values into single-element arrays.
const attributeXml = '<item category="books" tags="fiction"/>';
const attributeResult = configuredParser.parse(attributeXml);

assert.strictEqual(attributeResult.item['@_category'], 'books');
assert.strictEqual(Array.isArray(attributeResult.item['@_tags']), true);
assert.strictEqual(attributeResult.item['@_tags'].length, 1);
assert.deepStrictEqual(attributeResult.item['@_tags'], ['fiction']);

// Contract 5: Single-element nodes configured via isArray maintain array methods like map and filter without requiring runtime type guards.
const configuredMultiResult = configuredParser.parse(multiXml);

assert.strictEqual(Array.isArray(configuredMultiResult.feed.entry), true);
assert.strictEqual(configuredMultiResult.feed.entry.length, 2);

const singleMapped = configuredSingleResult.feed.entry.map(entry => entry.id);
const multiMapped = configuredMultiResult.feed.entry.map(entry => entry.id);
const singleFiltered = configuredSingleResult.feed.entry.filter(entry => entry.id === 101);

assert.deepStrictEqual(singleMapped, [101]);
assert.deepStrictEqual(multiMapped, [101, 102]);
assert.strictEqual(singleFiltered.length, 1);
assert.strictEqual(singleFiltered[0].title, 'Item 1');

Seeder de origen

csx-seed