🐥

JavaScript で XML を扱う | NodeJS + fast-xml-parser

2024/12/12に公開

https://github.com/NaturalIntelligence/fast-xml-parser

fast-xml-parser でできること

  • XML をオブジェクトに変換する(パース)
  • オブジェクトを XML に変換する(ビルド)
  • XML のフォーマットを検証する(バリデーション)

前提

  • Node: 20.12.2
  • fast-xml-parser: ^4.5.0

セットアップ

npm i fast-xml-parser

使い方

XML をオブジェクトに変換する(パース)

index.js
const { XMLParser } = require('fast-xml-parser');

const options = {
    ignoreAttributes: false, // 属性値がデフォルトでは無視されてしまうので有効化
};
const parser = new XMLParser(options);

// 以下の XML 文字列を使用する.
const xmlData = `
    <?xml version="1.0" encoding="UTF-8"?>
    <catalog>
        <product>
            <id>1</id> <name>Widget A</name> <price>25.00</price>
            <description>A high-quality widget.</description>
            <inStock>true</inStock>
        </product>
        <product>
            <id>2</id> <name>Gadget B</name> <price>15.50</price>
            <description>An affordable gadget.</description>
            <inStock>false</inStock>
        </product>
    </catalog>
`;

const outputObject = parser.parse(xmlData);
console.log(outputObject);

上記を実行すると以下のようなオブジェクトが得られる.

{
    '?xml': {
        '@_version': '1.0',
        '@_encoding': 'UTF-8'
    },
    catalog: {
        product: [
            {
                id: 1, name: 'Widget A', price: 25,
                description: 'A high-quality widget.',
                inStock: true
            },
            {
                id: 2, name: 'Gadget B', price: 15.5,
                description: 'An affordable gadget.',
                inStock: false
            }
        ]
    }
}

オブジェクトを XML に変換する(ビルド)

index.js
const { XMLBuilder } = require('fast-xml-parser');
const options = {
    ignoreAttributes: false, // 属性値がデフォルトでは無視されてしまうので有効化
};
const builder = new XMLBuilder(options);

// 以下のオブジェクトを使用する.
const objectData = {
    '?xml': {
        '@_version': '1.0',
        '@_encoding': 'UTF-8'
    },
    catalog: {
        product: [
            {
                id: 1, name: 'Widget A', price: 25,
                description: 'A high-quality widget.',
                inStock: true
            },
            {
                id: 2, name: 'Gadget B', price: 15.5,
                description: 'An affordable gadget.',
                inStock: false
            }
        ]
    }
}

const outputXML = builder.build(objectData);
console.log(outputXML);

上記を実行すると以下のような XML 文字列が得られる.

<?xml version="1.0" encoding="UTF-8"?>
<catalog>
    <product>
        <id>1</id> <name>Widget A</name> <price>25</price>
        <description>A high-quality widget.</description>
        <inStock>true</inStock>
    </product>
    <product>
        <id>2</id> <name>Gadget B</name> <price>15.5</price>
        <description>An affordable gadget.</description>
        <inStock>false</inStock>
    </product>
</catalog>

XML のフォーマットを検証する(バリデーション)

index.js
const { XMLValidator } = require('fast-xml-parser');

// 以下の XML 文字列を使用する.
// XML 宣言はドキュメントの最初になければならない.
const xmlData = `
    <?xml version="1.0" encoding="UTF-8"?>
    <catalog>
        <product>
            <id>1</id> <name>Widget A</name> <price>25.00</price>
            <description>A high-quality widget.</description>
            <inStock>true</inStock>
        </product>
        <product>
            <id>2</id> <name>Gadget B</name> <price>15.50</price>
            <description>An affordable gadget.</description>
            <inStock>false</inStock>
        </product>
    </catalog>
`;

const result = XMLValidator.validate(xmlData);
console.log(result);
{
    err: {
        code: 'InvalidXml',
        msg: 'XML declaration allowed only at the start of the document.',
        line: 2,
        col: 10
    }
}
index.js
const { XMLValidator } = require('fast-xml-parser');

// 以下の XML 文字列を使用する.
// XML 宣言はドキュメントの最初になければならない.
const xmlData = `<?xml version="1.0" encoding="UTF-8"?>
    <catalog>
        <product>
            <id>1</id> <name>Widget A</name> <price>25.00</price>
            <description>A high-quality widget.</description>
            <inStock>true</inStock>
        </product>
        <product>
            <id>2</id> <name>Gadget B</name> <price>15.50</price>
            <description>An affordable gadget.</description>
            <inStock>false</inStock>
        </product>
    </catalog>
`;

const result = XMLValidator.validate(xmlData);
console.log(result);
true
index.js
const { XMLValidator } = require('fast-xml-parser');

// 以下の XML 文字列を使用する.
// inStock の閉じタグがない.
const xmlData = `<?xml version="1.0" encoding="UTF-8"?>
    <catalog>
        <product>
            <id>1</id> <name>Widget A</name> <price>25.00</price>
            <description>A high-quality widget.</description>
            <inStock>true</inStock>
        </product>
        <product>
            <id>2</id> <name>Gadget B</name> <price>15.50</price>
            <description>An affordable gadget.</description>
            <inStock>false<inStock>
        </product>
    </catalog>
`;

const result = XMLValidator.validate(xmlData);
console.log(result);
{
    err: {
        code: 'InvalidXml',
        msg: 'XML declaration allowed only at the start of the document.',
        line: 2,
        col: 10
    }
}

XMLValidator には、以下のようにオプションを指定できる.

const result = XMLValidator.validate(xmlData, options);

代表的なオプションは unpairedTags.
閉じタグを持たないタグを指定できる. 例えば <br>.
XMLParser, XMLBuilder, XMLValidator でこのオプションが使用可能.

const xmlData = `<parent><extra></parent>`;
const options = {
    unpairedTags: ['extra'],
}
const result = XMLValidator.validate(xmlData, options);

なお、XMLValidator はエラー時、エラーを投げるので、try catchなどが使用可能である.

主なオプション

ignoreAttributes

属性値(<a xx="this is attr">)を無視する. デフォルトは true.

ignoreAttributes: false

attributeNamePrefix

ignoreAttributes オプションを false にしたときの、属性のキーにつけるプレフィックス.
デフォルトは @_. XMLParser, XMLBuilder で使用可能.

attributeNamePrefix : '@_'
// <aaa xxx="nice">wow</aaa> -> { '@_xxx': 'nice', aaa: 'wow' }

allowBooleanAttributes

値を持たない属性を許可する. true にしたとき、属性=true として扱われる.
デフォルトは false. XMLParser, XMLBuilder で使用可能.

allowBooleanAttributes: true
// <aaa checked>wow</aaa> -> { '@_checked': true, aaa: 'wow' }
allowBooleanAttributes: false
// <aaa checked>wow</aaa> -> { aaa: 'wow' }

alwaysCreateTextNode

テキストノードが存在するとき、常にテキストノード(#text)を作成する. デフォルトは false.

alwaysCreateTextNode: false
// <a>wow <b>hello</b> </a> -> { a: { b: 'hello', '#text': 'wow' }
alwaysCreateTextNode: true
// <a>wow <b>hello</b> </a>
// -> { a: { b: { '#text': 'hello' }, '#text': 'wow' } }

isArray

タグを配列とオブジェクトのどちらでパースするかを指定する. 関数を取り、戻り値が true なら配列として、false ならオブジェクトとしてパースされる.

index.js
const alwaysArray = [
    'a.b',
];
const options = {
    isArray: (tagName, tagPath, isLeafNode, isAttribute) => {
        console.log(tagName, tagPath, isLeafNode, isAttribute);
        return (alwaysArray.indexOf(tagPath) !== -1);
    }
};

const xmlData = `<a><b>hello</b></a>`;
const result = (new XMLParser(options)).parse(xmlData);
console.log(result);

アウトプットは以下.

b a.b true undefined
a a false undefined
{ a: { b: [ 'hello' ] } }

その他のオプション、詳しい説明は以下のドキュメントを参照
https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/docs/v4/2.XMLparseOptions.md

Discussion