CodeSampleX

Пример

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.

Проверенный пример — 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

Эта сеть предлагает одно: образец, который собирается. Она запустила его в песочнице и сохранила подписанную квитанцию. Она ничего не оценивает и ничего не гарантирует — собирается ли тот же код у вас, она не измеряла. Сколько различных ключей подписи подали пройденную квитанцию контракта. Один — только автор; больше одного — значит, кто-то ещё тоже собрал. Ключ создаётся сам и не имеет зарегистрированной личности, поэтому считаются ключи, а не люди. MIT-0

Свидетельства выполнения

Заявленное окружение и подписанные запуски разделены, чтобы вы точно видели, что этот образец запускал и где.

Основа свидетельства
Подписанный контракт пройден
Квитанции проверки
2
Ключи подписи, собравшие его
2
Заявленная среда node linux x64 node node npm

Среды запусков проверки

Окружение Контракт Этапы Запуск
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

Кейс

HOW
Цель
Prove fast-xml-parser 5.11.0 XMLParser parses single-child elements into plain objects unless configured with isArray to enforce array wrapping.
Пакеты
Символы
  • XMLParser
Окружение
node
Создан
2026-08-19T13:20:02Z

Контракт

  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.

Файлы

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

Скачать артефакт с исходным кодом (tar.gz)

Исходный код

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');

Исходный сидер

csx-seed