🐥
JavaScript で XML を扱う | NodeJS + 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' ] } }
その他のオプション、詳しい説明は以下のドキュメントを参照



Discussion