Skip to content

Commit 1ed2965

Browse files
committed
feat: add tagNameProcessor and attributeNameProcessor
1 parent 89563c5 commit 1ed2965

File tree

5 files changed

+75
-33
lines changed

5 files changed

+75
-33
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ object = {
5959
| cdataTagName | If specified, parser parse CDATA as nested tag instead of adding it's value to parent tag. | `false` |
6060
| arrayMode | When `false`, a tag with single occurrence is parsed as an object but as an array in case of multiple occurences. When `true`, a tag will be parsed as an array always excluding leaf nodes. When `strict`, all the tags will be parsed as array only. When instance of `RegEx`, only tags will be parsed as array that match the regex. When `function` a tag name is passed to the callback that can be checked. | `false` |
6161
| tagValueProcessor | Process tag value during transformation. Like HTML decoding, word capitalization, etc. Applicable in case of string only. | `(value) => decoder.decode(value).replace(/\r/g, '')` |
62-
| attributeValueProcessor | Process attribute value during transformation. Like HTML decoding, word capitalization, etc. Applicable in case of string only. | `(value) => value` |
62+
| attributeValueProcessor | Process attribute value during transformation. Like HTML decoding, word capitalization, etc. | `(value) => value` |
6363
| stopNodes | an array of tag names which are not required to be parsed. They are kept as Uint8Array. | `[]` |
6464

6565
## [API Documentation](https://cheminfo.github.io/arraybuffer-xml-parser/)

package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,19 @@
3535
},
3636
"devDependencies": {
3737
"@babel/plugin-transform-modules-commonjs": "^7.15.4",
38-
"@types/jest": "^27.0.1",
38+
"@types/jest": "^27.0.2",
3939
"cheminfo-build": "^1.1.11",
40-
"eslint": "^7.32.0",
41-
"eslint-config-cheminfo": "^5.4.0",
40+
"eslint": "^8.0.1",
41+
"eslint-config-cheminfo": "^7.1.1",
4242
"he": "^1.2.0",
43-
"iobuffer": "^5.0.3",
44-
"jest": "^27.1.0",
43+
"iobuffer": "^5.0.4",
44+
"jest": "^27.3.1",
4545
"pako": "^2.0.4",
46-
"prettier": "^2.3.2",
47-
"rollup": "^2.56.3",
46+
"prettier": "^2.4.1",
47+
"rollup": "^2.58.0",
4848
"uint8-base64": "^0.1.1"
4949
},
5050
"dependencies": {
51-
"dynamic-typing": "^0.1.2"
51+
"dynamic-typing": "^0.1.3"
5252
}
5353
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/* eslint-disable camelcase */
2+
/* eslint-disable no-tabs */
3+
import he from 'he';
4+
5+
import { parse } from '../parse';
6+
7+
describe('XMLParser', () => {
8+
it('tag name processor', () => {
9+
const xmlData = `<?xml version='1.0'?>
10+
<any_name>
11+
<person>
12+
end
13+
</person>
14+
</any_name>`;
15+
16+
const result = parse(xmlData, {
17+
tagNameProcessor: (name) => name.toUpperCase(),
18+
});
19+
expect(result).toStrictEqual({ ANY_NAME: { PERSON: 'end' } });
20+
});
21+
22+
it('attribute name processor', () => {
23+
const xmlData = `<?xml version='1.0'?>
24+
<ab param1="abc" param2="def">
25+
</ab>`;
26+
27+
const result = parse(xmlData, {
28+
attributeNameProcessor: (name) => name.toUpperCase(),
29+
});
30+
expect(result).toStrictEqual({ ab: { $PARAM1: 'abc', $PARAM2: 'def' } });
31+
});
32+
});

src/parse.js

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,24 @@ import { traversableToJSON } from './traversableToJSON';
44

55
/**
66
* Parse an ArrayBuffer or Uint8Array representing an XML
7-
* @param {ArrayBuffer|Uint8Arra} xmlData
7+
* @param {string|ArrayBuffer|Uint8Array} xmlData
88
* @param {object} [options={}]
9-
* @param {string} [attributeNamePrefix='$']
10-
* @param {boolean} [attributesNodeName=false]
11-
* @param {string} [textNodeName='#text']
12-
* @param {boolean} [trimValues=true] should we remove ascii < 32
13-
* @param {boolean} [ignoreAttributes=false] skip attributes
14-
* @param {boolean} [ignoreNameSpace=false]
15-
* @param {boolean} [dynamicTypingAttributeValue=true] Parse attribute values that looks like number or boolean
16-
* @param {boolean} [allowBooleanAttributes=false]
17-
* @param {boolean} [dynamicTypingNodeValue=true] Parse tag values that looks like number or boolean
18-
* @param {boolean} [arrayMode=false]
19-
* @param {boolean} [cdataTagName=false]
20-
* @param {function} [tagValueProcessor=(v, node) => decoder.decode(v)] Tag values can be modified during parsing. By default we decode the tag value (a Uint8Array) using TextDecoder
21-
* @param {function} [attributeValueProcessor=(v) => v] Attribute values can be modified during parsing
22-
* @param {boolean} [stopNodes=[]] prevent further parsing
9+
* @param {string} [options.attributeNamePrefix='$']
10+
* @param {boolean} [options.attributesNodeName=false]
11+
* @param {string} [options.textNodeName='#text']
12+
* @param {boolean} [options.trimValues=true] should we remove ascii < 32
13+
* @param {boolean} [options.ignoreAttributes=false] skip attributes
14+
* @param {boolean} [options.ignoreNameSpace=false]
15+
* @param {boolean} [options.dynamicTypingAttributeValue=true] Parse attribute values that looks like number or boolean
16+
* @param {boolean} [options.allowBooleanAttributes=false]
17+
* @param {boolean} [options.dynamicTypingNodeValue=true] Parse tag values that looks like number or boolean
18+
* @param {boolean} [options.arrayMode=false]
19+
* @param {boolean} [options.cdataTagName=false]
20+
* @param {function} [options.tagValueProcessor=(v, node) => decoder.decode(v)] Tag values can be modified during parsing. By default we decode the tag value (a Uint8Array) using TextDecoder
21+
* @param {function} [options.attributeValueProcessor=(v) => v] Attribute values can be modified during parsing
22+
* @param {function} [options.tagNameProcessor=(v) => v] Callback allowing to rename tag names
23+
* @param {function} [options.attributeNameProcessor=(v) => v] Callback allowing to rename attribute name
24+
* @param {boolean} [options.stopNodes=[]] prevent further parsing
2325
*
2426
* @returns {object}
2527
*/

src/traversableToJSON.js

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ import { isTagNameInArrayMode, merge, isEmptyObject } from './util';
1010
* @returns
1111
*/
1212
export function traversableToJSON(node, options, parentTagName) {
13-
const { dynamicTypingNodeValue, tagValueProcessor, arrayMode } = options;
13+
const {
14+
dynamicTypingNodeValue,
15+
tagValueProcessor,
16+
arrayMode,
17+
tagNameProcessor,
18+
attributeNameProcessor,
19+
} = options;
1420
const result = {};
1521

1622
if (tagValueProcessor) {
@@ -43,9 +49,12 @@ export function traversableToJSON(node, options, parentTagName) {
4349
if (options.attributeNamePrefix) {
4450
// need to rename the attributes
4551
const renamedAttributes = {};
46-
for (let key in node.attributes) {
47-
renamedAttributes[options.attributeNamePrefix + key] =
48-
node.attributes[key];
52+
for (let attributeName in node.attributes) {
53+
const newAttributeName = attributeNameProcessor
54+
? attributeNameProcessor(attributeName)
55+
: attributeName;
56+
renamedAttributes[options.attributeNamePrefix + newAttributeName] =
57+
node.attributes[attributeName];
4958
}
5059
attributes = renamedAttributes;
5160
}
@@ -57,14 +66,13 @@ export function traversableToJSON(node, options, parentTagName) {
5766
merge(result, attributes, arrayMode);
5867
}
5968

60-
const keys = Object.keys(node.children);
61-
for (let index = 0; index < keys.length; index++) {
62-
const tagName = keys[index];
69+
for (const tagName in node.children) {
70+
const newTagName = tagNameProcessor ? tagNameProcessor(tagName) : tagName;
6371
if (node.children[tagName] && node.children[tagName].length > 1) {
6472
result[tagName] = [];
6573
for (let tag in node.children[tagName]) {
6674
if (Object.prototype.hasOwnProperty.call(node.children[tagName], tag)) {
67-
result[tagName].push(
75+
result[newTagName].push(
6876
traversableToJSON(node.children[tagName][tag], options, tagName),
6977
);
7078
}
@@ -78,7 +86,7 @@ export function traversableToJSON(node, options, parentTagName) {
7886
const asArray =
7987
(arrayMode === true && typeof subResult === 'object') ||
8088
isTagNameInArrayMode(tagName, arrayMode, parentTagName);
81-
result[tagName] = asArray ? [subResult] : subResult;
89+
result[newTagName] = asArray ? [subResult] : subResult;
8290
}
8391
}
8492

0 commit comments

Comments
 (0)