backend v4 half
This commit is contained in:
+77
@@ -1,5 +1,82 @@
|
||||
<small>Note: If you find missing information about particular minor version, that version must have been changed without any functional change in this library.</small>
|
||||
|
||||
**5.2.5 / 2025-06-08**
|
||||
- Inform user to use [fxp-cli](https://github.com/NaturalIntelligence/fxp-cli) instead of in-built CLI feature
|
||||
- Export typings for direct use
|
||||
|
||||
**5.2.4 / 2025-06-06**
|
||||
- fix (#747): fix EMPTY and ANY with ELEMENT in DOCTYPE
|
||||
|
||||
**5.2.3 / 2025-05-11**
|
||||
- fix (#747): support EMPTY and ANY with ELEMENT in DOCTYPE
|
||||
|
||||
**5.2.2 / 2025-05-05**
|
||||
- fix (#746): update strnum to fix parsing issues related to enotations
|
||||
|
||||
**5.2.1 / 2025-04-22**
|
||||
- fix: read DOCTYPE entity value correctly
|
||||
- read DOCTYPE NOTATION, ELEMENT exp but not using read values
|
||||
|
||||
|
||||
**5.2.0 / 2025-04-03**
|
||||
- feat: support metadata on nodes (#593) (By [Steven R. Loomis](https://github.com/srl295))
|
||||
|
||||
**5.1.0 / 2025-04-02**
|
||||
- feat: declare package as side-effect free (#738) (By [Thomas Bouffard](https://github.com/tbouffard))
|
||||
- fix cjs build mode
|
||||
- fix builder return type to string
|
||||
-
|
||||
|
||||
**5.0.9 / 2025-03-14**
|
||||
- fix: support numeric entities with values over 0xFFFF (#726) (By [Marc Durdin](https://github.com/mcdurdin))
|
||||
- fix: update strnum to fix parsing 0 if skiplike option is used
|
||||
|
||||
**5.0.8 / 2025-02-27**
|
||||
- fix parsing 0 if skiplike option is used.
|
||||
- updating strnum dependency
|
||||
|
||||
**5.0.7 / 2025-02-25**
|
||||
- fix (#724) typings for cjs.
|
||||
|
||||
**5.0.6 / 2025-02-20**
|
||||
- fix cli output (By [Angel Delgado](https://github.com/angeld7))
|
||||
- remove multiple JSON parsing
|
||||
|
||||
**5.0.5 / 2025-02-20**
|
||||
- fix parsing of string starting with 'e' or 'E' by updating strnum
|
||||
|
||||
**5.0.4 / 2025-02-20**
|
||||
- fix CLI to support all the versions of node js when displaying library version.
|
||||
- fix CJS import in v5
|
||||
- by fixing webpack config
|
||||
|
||||
**5.0.3 / 2025-02-20**
|
||||
- Using strnum ESM module
|
||||
- new fixes in strum may break your experience
|
||||
|
||||
**5.0.2 / 2025-02-20**
|
||||
- fix: include CommonJS resources in the npm package #714 (By [Thomas Bouffard](https://github.com/tbouffard))
|
||||
- fix: move babel deps to dev deps
|
||||
|
||||
**5.0.1 / 2025-02-19**
|
||||
- fix syntax error for CLI command
|
||||
|
||||
**5.0.0 / 2025-02-19**
|
||||
- ESM support
|
||||
- no change in the functionality, syntax, APIs, options, or documentation.
|
||||
|
||||
**4.5.2 / 2025-02-18**
|
||||
- Fix null CDATA to comply with undefined behavior (#701) (By [Matthieu BOHEAS](https://github.com/Kelgors))
|
||||
- Fix(performance): Update check for leaf node in saveTextToParentTag function in OrderedObjParser.js (#707) (By [...](https://github.com/tomingtoming))
|
||||
- Fix: emit full JSON string from CLI when no output filename specified (#710) (By [Matt Benson](https://github.com/mbenson))
|
||||
|
||||
**4.5.1 / 2024-12-15**
|
||||
- Fix empty tag key name for v5 (#697). no impact on v4
|
||||
- Fixes entity parsing when used in strict mode (#699)
|
||||
|
||||
**4.5.0 / 2024-09-03**
|
||||
- feat #666: ignoreAttributes support function, and array of string or regex (By [ArtemM](https://github.com/mav-rik))
|
||||
|
||||
**4.4.1 / 2024-07-28**
|
||||
- v5 fix: maximum length limit to currency value
|
||||
- fix #634: build attributes with oneListGroup and attributesGroupName (#653)(By [Andreas Naziris](https://github.com/a-rasin))
|
||||
|
||||
+40
-53
@@ -1,17 +1,29 @@
|
||||
# [fast-xml-parser](https://www.npmjs.com/package/fast-xml-parser)
|
||||
[![NPM quality][quality-image]][quality-url]
|
||||
[](https://coveralls.io/github/NaturalIntelligence/fast-xml-parser?branch=master)
|
||||
[<img src="https://img.shields.io/badge/Try-me-blue.svg?colorA=FFA500&colorB=0000FF" alt="Try me"/>](https://naturalintelligence.github.io/fast-xml-parser/)
|
||||
[](https://npm.im/fast-xml-parser)
|
||||
|
||||
[](https://npm.im/fast-xml-parser)
|
||||
|
||||
Validate XML, Parse XML to JS Object, or Build XML from JS Object without C/C++ based libraries and no callback.
|
||||
|
||||
---
|
||||
<img align="right" src="static/img/fxp_logo.png" width="180px" alt="FXP logo"/>
|
||||
|
||||
<a href="https://www.amazon.in/Power-Glasses-world-imagination-Perspective-ebook/dp/B0CW1CJGNK/"><img align="left" src="https://github.com/NaturalIntelligence/fast-xml-parser/assets/7692328/e7a42bcc-5186-45e3-bfee-de8d8b9a69d4" alt="ads-thePowerGlassesBook"/></a>
|
||||
I had recently published a book, The Power Glasses. Please have a look. Your feedback would be helpful. You can [mail](githubissues@proton.me) me for a free copy.
|
||||
<br>
|
||||
* Validate XML data syntactically. Use [detailed-xml-validator](https://github.com/NaturalIntelligence/detailed-xml-validator/) to verify business rules.
|
||||
* Parse XML to JS Objectand vice versa
|
||||
* Common JS, ESM, and browser compatible
|
||||
* Faster than any other pure JS implementation.
|
||||
|
||||
It can handle big files (tested up to 100mb). XML Entities, HTML entities, and DOCTYPE entites are supported. Unpaired tags (Eg `<br>` in HTML), stop nodes (Eg `<script>` in HTML) are supported. It can also preserve Order of tags in JS object
|
||||
|
||||
---
|
||||
# Your Support, Our Motivation
|
||||
|
||||
## Try out our New Thoughts
|
||||
|
||||
We've recently launched **Flowgger**
|
||||
<a href="https://github.com/solothought/flowgger"> <img src="static/img/flowgger_h.webp" alt="Flowgger Logging Framework" /></a>
|
||||
|
||||
Don't forget to check our new library [Text2Chart](https://solothought.com/text2chart/flow) that constructs flow chart out of simple text. Very helpful in creating or alayzing an algorithm, and documentation purpose.
|
||||
|
||||
## Financial Support
|
||||
|
||||
Sponsor this project
|
||||
|
||||
@@ -21,7 +33,7 @@ Sponsor this project
|
||||
<a href="https://opencollective.com/fast-xml-parser/donate" target="_blank">
|
||||
<img src="https://opencollective.com/fast-xml-parser/donate/button@2x.png?color=blue" width=180 />
|
||||
</a>
|
||||
<a href="https://paypal.me/naturalintelligence"> <img src="static/img/support_paypal.svg" alt="Stubmatic donate button" width="180"/></a>
|
||||
<a href="https://paypal.me/naturalintelligence"> <img src="static/img/support_paypal.svg" alt="donate button" width="180"/></a>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
@@ -50,7 +62,9 @@ Through OpenCollective
|
||||
<a href="https://opencollective.com/fast-xml-parser/sponsor/9/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/9/avatar.svg"></a>
|
||||
-->
|
||||
|
||||

|
||||

|
||||
|
||||
> This is a donation. No goods or services are expected in return. Any requests for refunds for those purposes will be rejected.
|
||||
|
||||
## Users
|
||||
|
||||
@@ -78,30 +92,7 @@ Through OpenCollective
|
||||
|
||||
---
|
||||
|
||||
## Main Features
|
||||
|
||||
<img align="right" src="static/img/fxp_logo.png" width="180px" alt="FXP logo"/>
|
||||
|
||||
* Validate XML data syntactically
|
||||
* Parse XML to JS Object
|
||||
* Build XML from JS Object
|
||||
* Compatible to node js packages, in browser, and in CLI (click try me button above for demo)
|
||||
* Faster than any other pure JS implementation.
|
||||
* It can handle big files (tested up to 100mb).
|
||||
* Controlled parsing using various options
|
||||
* XML Entities, HTML entities, and DOCTYPE entites are supported.
|
||||
* unpaired tags (Eg `<br>` in HTML), stop nodes (Eg `<script>` in HTML) are supported.
|
||||
* You can restore almost same XML from JSON
|
||||
* Supports comments
|
||||
* It can preserve Order of tags in JS object
|
||||
* You can control if a single tag should be parsed into array.
|
||||
* Supports parsing of PI (Processing Instruction) tags with XML declaration tags
|
||||
* And many more other features.
|
||||
|
||||
## v5
|
||||
I developed v5 in Apr 2023. And I didn't get the chance to complete all the features. I've ensured that new features don't impact performance. With v5, you have more control on parsing output. Check [docs](./docs/v5) for syntax help and basic understanding.
|
||||
|
||||
Please leave a comment in discussion forum for your suggestions and if you really need v5.
|
||||
# More about this library
|
||||
|
||||
## How to use
|
||||
|
||||
@@ -152,9 +143,9 @@ Bundle size
|
||||
| fxp.min.js | 26K |
|
||||
| fxvalidator.min.js | 5.7K |
|
||||
|
||||
### Documents
|
||||
## Documents
|
||||
<table>
|
||||
<tr><td>v3</td><td>v4</td><td>v5</td></tr>
|
||||
<tr><td>v3</td><td>v4 and v5</td><td>v6</td></tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="./docs/v3/docs.md">documents</a>
|
||||
@@ -169,16 +160,18 @@ Bundle size
|
||||
<li><a href="./docs/v4/7.PITags.md">PI Tag processing</a></li>
|
||||
</ol></td>
|
||||
<td><ol>
|
||||
<li></li><a href="./docs/v5/1.GettingStarted.md">Getting Started</a></li>
|
||||
<li><a href="./docs/v5/2.Features.md">Features</a></li>
|
||||
<li><a href="./docs/v5/3.Options.md">Options</a></li>
|
||||
<li><a href="./docs/v5/4.OutputBuilders.md">Output Builders</a></li>
|
||||
<li><a href="./docs/v5/5.ValueParsers.md">Value Parsers</a></li>
|
||||
<li></li><a href="./docs/v6/1.GettingStarted.md">Getting Started</a></li>
|
||||
<li><a href="./docs/v6/2.Features.md">Features</a></li>
|
||||
<li><a href="./docs/v6/3.Options.md">Options</a></li>
|
||||
<li><a href="./docs/v6/4.OutputBuilders.md">Output Builders</a></li>
|
||||
<li><a href="./docs/v6/5.ValueParsers.md">Value Parsers</a></li>
|
||||
</ol></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**note**: version 5 is released with version 4 tfor experimental use. Based on it's demand, it'll be developed and the features can be different in final release.
|
||||
**note**:
|
||||
- Version 6 is released with version 4 for experimental use. Based on it's demand, it'll be developed and the features can be different in final release.
|
||||
- Version 5 has the same functionalities as version 4.
|
||||
|
||||
## Performance
|
||||
<small>negative means error</small>
|
||||
@@ -196,10 +189,9 @@ Bundle size
|
||||
<img src="./docs/imgs/XMLBuilder_v4.png" width="50%" />
|
||||
* Y-axis: requests per second
|
||||
|
||||
|
||||
|
||||
<!-- [](https://github.com/NaturalIntelligence/ads/) -->
|
||||
|
||||
---
|
||||
|
||||
## Usage Trend
|
||||
|
||||
@@ -209,20 +201,15 @@ Bundle size
|
||||
<img src="https://npm-compare.com/img/npm-trend/THREE_YEARS/fast-xml-parser.png" width="50%" alt="NPM Usage Trend of fast-xml-parser" />
|
||||
</a>
|
||||
|
||||
## Supporters
|
||||
### Contributors
|
||||
# Supporters
|
||||
#### Contributors
|
||||
|
||||
This project exists thanks to [all](graphs/contributors) the people who contribute. [[Contribute](docs/CONTRIBUTING.md)].
|
||||
<!-- <a href="graphs/contributors"><img src="https://opencollective.com/fast-xml-parser/contributors.svg?width=890&button=false" /></a> -->
|
||||
<!--
|
||||
### Lead Maintainers
|
||||

|
||||
[](https://github.com/Delagen)
|
||||
|
||||
### All Contributors -->
|
||||
<a href="graphs/contributors"><img src="https://opencollective.com/fast-xml-parser/contributors.svg?width=890&button=false" /></a>
|
||||
|
||||
### Backers
|
||||
|
||||
#### Backers from Open collective
|
||||
|
||||
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/fast-xml-parser#backer)]
|
||||
|
||||
|
||||
+2
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+458
@@ -0,0 +1,458 @@
|
||||
type X2jOptions = {
|
||||
/**
|
||||
* Preserve the order of tags in resulting JS object
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
preserveOrder?: boolean;
|
||||
|
||||
/**
|
||||
* Give a prefix to the attribute name in the resulting JS object
|
||||
*
|
||||
* Defaults to '@_'
|
||||
*/
|
||||
attributeNamePrefix?: string;
|
||||
|
||||
/**
|
||||
* A name to group all attributes of a tag under, or `false` to disable
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
attributesGroupName?: false | string;
|
||||
|
||||
/**
|
||||
* The name of the next node in the resulting JS
|
||||
*
|
||||
* Defaults to `#text`
|
||||
*/
|
||||
textNodeName?: string;
|
||||
|
||||
/**
|
||||
* Whether to ignore attributes when parsing
|
||||
*
|
||||
* When `true` - ignores all the attributes
|
||||
*
|
||||
* When `false` - parses all the attributes
|
||||
*
|
||||
* When `Array<string | RegExp>` - filters out attributes that match provided patterns
|
||||
*
|
||||
* When `Function` - calls the function for each attribute and filters out those for which the function returned `true`
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
ignoreAttributes?: boolean | (string | RegExp)[] | ((attrName: string, jPath: string) => boolean);
|
||||
|
||||
/**
|
||||
* Whether to remove namespace string from tag and attribute names
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
removeNSPrefix?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to allow attributes without value
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
allowBooleanAttributes?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to parse tag value with `strnum` package
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
parseTagValue?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to parse tag value with `strnum` package
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
parseAttributeValue?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to remove surrounding whitespace from tag or attribute value
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
trimValues?: boolean;
|
||||
|
||||
/**
|
||||
* Give a property name to set CDATA values to instead of merging to tag's text value
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
cdataPropName?: false | string;
|
||||
|
||||
/**
|
||||
* If set, parse comments and set as this property
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
commentPropName?: false | string;
|
||||
|
||||
/**
|
||||
* Control how tag value should be parsed. Called only if tag value is not empty
|
||||
*
|
||||
* @returns {undefined|null} `undefined` or `null` to set original value.
|
||||
* @returns {unknown}
|
||||
*
|
||||
* 1. Different value or value with different data type to set new value.
|
||||
* 2. Same value to set parsed value if `parseTagValue: true`.
|
||||
*
|
||||
* Defaults to `(tagName, val, jPath, hasAttributes, isLeafNode) => val`
|
||||
*/
|
||||
tagValueProcessor?: (tagName: string, tagValue: string, jPath: string, hasAttributes: boolean, isLeafNode: boolean) => unknown;
|
||||
|
||||
/**
|
||||
* Control how attribute value should be parsed
|
||||
*
|
||||
* @param attrName
|
||||
* @param attrValue
|
||||
* @param jPath
|
||||
* @returns {undefined|null} `undefined` or `null` to set original value
|
||||
* @returns {unknown}
|
||||
*
|
||||
* Defaults to `(attrName, val, jPath) => val`
|
||||
*/
|
||||
attributeValueProcessor?: (attrName: string, attrValue: string, jPath: string) => unknown;
|
||||
|
||||
/**
|
||||
* Options to pass to `strnum` for parsing numbers
|
||||
*
|
||||
* Defaults to `{ hex: true, leadingZeros: true, eNotation: true }`
|
||||
*/
|
||||
numberParseOptions?: strnumOptions;
|
||||
|
||||
/**
|
||||
* Nodes to stop parsing at
|
||||
*
|
||||
* Defaults to `[]`
|
||||
*/
|
||||
stopNodes?: string[];
|
||||
|
||||
/**
|
||||
* List of tags without closing tags
|
||||
*
|
||||
* Defaults to `[]`
|
||||
*/
|
||||
unpairedTags?: string[];
|
||||
|
||||
/**
|
||||
* Whether to always create a text node
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
alwaysCreateTextNode?: boolean;
|
||||
|
||||
/**
|
||||
* Determine whether a tag should be parsed as an array
|
||||
*
|
||||
* @param tagName
|
||||
* @param jPath
|
||||
* @param isLeafNode
|
||||
* @param isAttribute
|
||||
* @returns {boolean}
|
||||
*
|
||||
* Defaults to `() => false`
|
||||
*/
|
||||
isArray?: (tagName: string, jPath: string, isLeafNode: boolean, isAttribute: boolean) => boolean;
|
||||
|
||||
/**
|
||||
* Whether to process default and DOCTYPE entities
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
processEntities?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to process HTML entities
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
htmlEntities?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to ignore the declaration tag from output
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
ignoreDeclaration?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to ignore Pi tags
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
ignorePiTags?: boolean;
|
||||
|
||||
/**
|
||||
* Transform tag names
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
transformTagName?: ((tagName: string) => string) | false;
|
||||
|
||||
/**
|
||||
* Transform attribute names
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
transformAttributeName?: ((attributeName: string) => string) | false;
|
||||
|
||||
/**
|
||||
* Change the tag name when a different name is returned. Skip the tag from parsed result when false is returned.
|
||||
* Modify `attrs` object to control attributes for the given tag.
|
||||
*
|
||||
* @returns {string} new tag name.
|
||||
* @returns false to skip the tag
|
||||
*
|
||||
* Defaults to `(tagName, jPath, attrs) => tagName`
|
||||
*/
|
||||
updateTag?: (tagName: string, jPath: string, attrs: {[k: string]: string}) => string | boolean;
|
||||
|
||||
/**
|
||||
* If true, adds a Symbol to all object nodes, accessible by {@link XMLParser.getMetaDataSymbol} with
|
||||
* metadata about each the node in the XML file.
|
||||
*/
|
||||
captureMetaData?: boolean;
|
||||
};
|
||||
|
||||
type strnumOptions = {
|
||||
hex: boolean;
|
||||
leadingZeros: boolean,
|
||||
skipLike?: RegExp,
|
||||
eNotation?: boolean
|
||||
}
|
||||
|
||||
type validationOptions = {
|
||||
/**
|
||||
* Whether to allow attributes without value
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
allowBooleanAttributes?: boolean;
|
||||
|
||||
/**
|
||||
* List of tags without closing tags
|
||||
*
|
||||
* Defaults to `[]`
|
||||
*/
|
||||
unpairedTags?: string[];
|
||||
};
|
||||
|
||||
type XmlBuilderOptions = {
|
||||
/**
|
||||
* Give a prefix to the attribute name in the resulting JS object
|
||||
*
|
||||
* Defaults to '@_'
|
||||
*/
|
||||
attributeNamePrefix?: string;
|
||||
|
||||
/**
|
||||
* A name to group all attributes of a tag under, or `false` to disable
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
attributesGroupName?: false | string;
|
||||
|
||||
/**
|
||||
* The name of the next node in the resulting JS
|
||||
*
|
||||
* Defaults to `#text`
|
||||
*/
|
||||
textNodeName?: string;
|
||||
|
||||
/**
|
||||
* Whether to ignore attributes when building
|
||||
*
|
||||
* When `true` - ignores all the attributes
|
||||
*
|
||||
* When `false` - builds all the attributes
|
||||
*
|
||||
* When `Array<string | RegExp>` - filters out attributes that match provided patterns
|
||||
*
|
||||
* When `Function` - calls the function for each attribute and filters out those for which the function returned `true`
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
ignoreAttributes?: boolean | (string | RegExp)[] | ((attrName: string, jPath: string) => boolean);
|
||||
|
||||
/**
|
||||
* Give a property name to set CDATA values to instead of merging to tag's text value
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
cdataPropName?: false | string;
|
||||
|
||||
/**
|
||||
* If set, parse comments and set as this property
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
commentPropName?: false | string;
|
||||
|
||||
/**
|
||||
* Whether to make output pretty instead of single line
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
format?: boolean;
|
||||
|
||||
|
||||
/**
|
||||
* If `format` is set to `true`, sets the indent string
|
||||
*
|
||||
* Defaults to ` `
|
||||
*/
|
||||
indentBy?: string;
|
||||
|
||||
/**
|
||||
* Give a name to a top-level array
|
||||
*
|
||||
* Defaults to `undefined`
|
||||
*/
|
||||
arrayNodeName?: string;
|
||||
|
||||
/**
|
||||
* Create empty tags for tags with no text value
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
suppressEmptyNode?: boolean;
|
||||
|
||||
/**
|
||||
* Suppress an unpaired tag
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
suppressUnpairedNode?: boolean;
|
||||
|
||||
/**
|
||||
* Don't put a value for boolean attributes
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
suppressBooleanAttributes?: boolean;
|
||||
|
||||
/**
|
||||
* Preserve the order of tags in resulting JS object
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
preserveOrder?: boolean;
|
||||
|
||||
/**
|
||||
* List of tags without closing tags
|
||||
*
|
||||
* Defaults to `[]`
|
||||
*/
|
||||
unpairedTags?: string[];
|
||||
|
||||
/**
|
||||
* Nodes to stop parsing at
|
||||
*
|
||||
* Defaults to `[]`
|
||||
*/
|
||||
stopNodes?: string[];
|
||||
|
||||
/**
|
||||
* Control how tag value should be parsed. Called only if tag value is not empty
|
||||
*
|
||||
* @returns {undefined|null} `undefined` or `null` to set original value.
|
||||
* @returns {unknown}
|
||||
*
|
||||
* 1. Different value or value with different data type to set new value.
|
||||
* 2. Same value to set parsed value if `parseTagValue: true`.
|
||||
*
|
||||
* Defaults to `(tagName, val, jPath, hasAttributes, isLeafNode) => val`
|
||||
*/
|
||||
tagValueProcessor?: (name: string, value: unknown) => unknown;
|
||||
|
||||
/**
|
||||
* Control how attribute value should be parsed
|
||||
*
|
||||
* @param attrName
|
||||
* @param attrValue
|
||||
* @param jPath
|
||||
* @returns {undefined|null} `undefined` or `null` to set original value
|
||||
* @returns {unknown}
|
||||
*
|
||||
* Defaults to `(attrName, val, jPath) => val`
|
||||
*/
|
||||
attributeValueProcessor?: (name: string, value: unknown) => unknown;
|
||||
|
||||
/**
|
||||
* Whether to process default and DOCTYPE entities
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
processEntities?: boolean;
|
||||
|
||||
|
||||
oneListGroup?: boolean;
|
||||
};
|
||||
|
||||
type ESchema = string | object | Array<string|object>;
|
||||
|
||||
type ValidationError = {
|
||||
err: {
|
||||
code: string;
|
||||
msg: string,
|
||||
line: number,
|
||||
col: number
|
||||
};
|
||||
};
|
||||
|
||||
declare class XMLParser {
|
||||
constructor(options?: X2jOptions);
|
||||
parse(xmlData: string | Buffer ,validationOptions?: validationOptions | boolean): any;
|
||||
/**
|
||||
* Add Entity which is not by default supported by this library
|
||||
* @param entityIdentifier {string} Eg: 'ent' for &ent;
|
||||
* @param entityValue {string} Eg: '\r'
|
||||
*/
|
||||
addEntity(entityIdentifier: string, entityValue: string): void;
|
||||
|
||||
/**
|
||||
* Returns a Symbol that can be used to access the {@link XMLMetaData}
|
||||
* property on a node.
|
||||
*
|
||||
* If Symbol is not available in the environment, an ordinary property is used
|
||||
* and the name of the property is here returned.
|
||||
*
|
||||
* The XMLMetaData property is only present when {@link X2jOptions.captureMetaData}
|
||||
* is true in the options.
|
||||
*/
|
||||
static getMetaDataSymbol() : Symbol;
|
||||
}
|
||||
|
||||
declare class XMLValidator{
|
||||
static validate(xmlData: string, options?: validationOptions): true | ValidationError;
|
||||
}
|
||||
|
||||
declare class XMLBuilder {
|
||||
constructor(options?: XmlBuilderOptions);
|
||||
build(jObj: any): string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This object is available on nodes via the symbol {@link XMLParser.getMetaDataSymbol}
|
||||
* when {@link X2jOptions.captureMetaData} is true.
|
||||
*/
|
||||
declare interface XMLMetaData {
|
||||
/** The index, if available, of the character where the XML node began in the input stream. */
|
||||
startIndex?: number;
|
||||
}
|
||||
|
||||
declare namespace fxp {
|
||||
export {
|
||||
XMLParser,
|
||||
XMLValidator,
|
||||
XMLBuilder,
|
||||
XMLMetaData
|
||||
}
|
||||
}
|
||||
|
||||
export = fxp;
|
||||
+2
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+36
-20
@@ -1,23 +1,43 @@
|
||||
{
|
||||
"name": "fast-xml-parser",
|
||||
"version": "4.4.1",
|
||||
"version": "5.2.5",
|
||||
"description": "Validate XML, Parse XML, Build XML without C/C++ based libraries",
|
||||
"main": "./src/fxp.js",
|
||||
"main": "./lib/fxp.cjs",
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
"module": "./src/fxp.js",
|
||||
"types": "./src/fxp.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./src/fxp.d.ts",
|
||||
"default": "./src/fxp.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./lib/fxp.d.cts",
|
||||
"default": "./lib/fxp.cjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "nyc --reporter=lcov --reporter=text jasmine spec/*spec.js",
|
||||
"test": "c8 --reporter=lcov --reporter=text jasmine spec/*spec.js",
|
||||
"test-types": "tsc --noEmit spec/typings/typings-test.ts",
|
||||
"unit": "jasmine",
|
||||
"coverage": "nyc report --reporter html --reporter text -t .nyc_output --report-dir .nyc_output/summary",
|
||||
"perf": "node ./benchmark/perfTest3.js",
|
||||
"lint": "eslint src/*.js spec/*.js",
|
||||
"bundle": "webpack --config webpack-prod.config.js",
|
||||
"lint": "eslint src/**/*.js spec/**/*.js benchmark/**/*.js",
|
||||
"bundle": "webpack --config webpack.cjs.config.js",
|
||||
"prettier": "prettier --write src/**/*.js",
|
||||
"publish-please": "publish-please",
|
||||
"checkReadiness": "publish-please --dry-run"
|
||||
},
|
||||
"bin": {
|
||||
"fxparser": "./src/cli/cli.js"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"src",
|
||||
"CHANGELOG.md"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/NaturalIntelligence/fast-xml-parser"
|
||||
@@ -31,7 +51,6 @@
|
||||
"x2js",
|
||||
"xml2json",
|
||||
"js",
|
||||
"cli",
|
||||
"validator",
|
||||
"validate",
|
||||
"transformer",
|
||||
@@ -49,26 +68,23 @@
|
||||
"@babel/register": "^7.13.8",
|
||||
"@types/node": "20",
|
||||
"babel-loader": "^8.2.2",
|
||||
"cytorus": "^0.2.9",
|
||||
"c8": "^10.1.3",
|
||||
"eslint": "^8.3.0",
|
||||
"he": "^1.2.0",
|
||||
"jasmine": "^3.6.4",
|
||||
"nyc": "^15.1.0",
|
||||
"prettier": "^1.19.1",
|
||||
"jasmine": "^5.6.0",
|
||||
"prettier": "^3.5.1",
|
||||
"publish-please": "^5.5.2",
|
||||
"typescript": "5",
|
||||
"webpack": "^5.64.4",
|
||||
"webpack-cli": "^4.9.1"
|
||||
},
|
||||
"typings": "src/fxp.d.ts",
|
||||
"funding": [{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||
},{
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/naturalintelligence"
|
||||
}],
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"strnum": "^1.0.5"
|
||||
"strnum": "^2.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
+16
-12
@@ -1,15 +1,21 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
/*eslint-disable no-console*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const {XMLParser, XMLValidator} = require("../fxp");
|
||||
const readToEnd = require('./read').readToEnd;
|
||||
import fs from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import {XMLParser, XMLValidator} from "../fxp.js";
|
||||
import ReadToEnd from './read.js';
|
||||
import cmdDetail from "./man.js"
|
||||
|
||||
console.warn("\x1b[33m%s\x1b[0m", "⚠️ Warning: The built-in CLI interface is now deprecated.");
|
||||
console.warn("Please install the dedicated CLI package instead:");
|
||||
console.warn(" npm install -g fxp-cli");
|
||||
|
||||
const version = require('./../../package.json').version;
|
||||
if (process.argv[2] === '--help' || process.argv[2] === '-h') {
|
||||
console.log(require("./man"));
|
||||
console.log(cmdDetail);
|
||||
} else if (process.argv[2] === '--version') {
|
||||
const packageJsonPath = resolve(process.cwd(), 'package.json');
|
||||
const version = JSON.parse(fs.readFileSync(packageJsonPath).toString()).version;
|
||||
console.log(version);
|
||||
} else {
|
||||
const options = {
|
||||
@@ -44,13 +50,10 @@ if (process.argv[2] === '--help' || process.argv[2] === '-h') {
|
||||
|
||||
const callback = function(xmlData) {
|
||||
let output = '';
|
||||
if (validate) {
|
||||
const parser = new XMLParser(options);
|
||||
output = parser.parse(xmlData,validate);
|
||||
} else if (validateOnly) {
|
||||
if (validateOnly) {
|
||||
output = XMLValidator.validate(xmlData);
|
||||
process.exitCode = output === true ? 0 : 1;
|
||||
} else {
|
||||
} else {
|
||||
const parser = new XMLParser(options);
|
||||
output = JSON.stringify(parser.parse(xmlData,validate), null, 4);
|
||||
}
|
||||
@@ -61,10 +64,11 @@ if (process.argv[2] === '--help' || process.argv[2] === '-h') {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
|
||||
if (!fileName) {
|
||||
readToEnd(process.stdin, function(err, data) {
|
||||
ReadToEnd.readToEnd(process.stdin, function(err, data) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,4 +1,9 @@
|
||||
module.exports = `Fast XML Parser 4.0.0
|
||||
import fs from 'fs';
|
||||
import { resolve } from 'path';
|
||||
const packageJsonPath = resolve(process.cwd(), 'package.json');
|
||||
const version = JSON.parse(fs.readFileSync(packageJsonPath).toString()).version;
|
||||
|
||||
export default `Fast XML Parser ${version}
|
||||
----------------
|
||||
$ fxparser [-ns|-a|-c|-v|-V] <filename> [-o outputfile.json]
|
||||
$ cat xmlfile.xml | fxparser [-ns|-a|-c|-v|-V] [-o outputfile.json]
|
||||
|
||||
+31
-80
@@ -1,92 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
// Copyright 2013 Timothy J Fontaine <tjfontaine@gmail.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the 'Software'), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE
|
||||
import { Transform } from 'stream';
|
||||
|
||||
/*
|
||||
|
||||
Read any stream all the way to the end and trigger a single cb
|
||||
|
||||
const http = require('http');
|
||||
|
||||
const rte = require('readtoend');
|
||||
|
||||
http.get('http://nodejs.org', function(response) {
|
||||
rte.readToEnd(response, function(err, body) {
|
||||
console.log(body);
|
||||
});
|
||||
});
|
||||
|
||||
*/
|
||||
|
||||
let stream = require('stream');
|
||||
const util = require('util');
|
||||
|
||||
if (!stream.Transform) {
|
||||
stream = require('readable-stream');
|
||||
}
|
||||
|
||||
function ReadToEnd(opts) {
|
||||
if (!(this instanceof ReadToEnd)) {
|
||||
return new ReadToEnd(opts);
|
||||
export default class ReadToEnd extends Transform {
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
this._encoding = options.encoding || 'utf8';
|
||||
this._buffer = '';
|
||||
}
|
||||
|
||||
stream.Transform.call(this, opts);
|
||||
|
||||
this._rte_encoding = opts.encoding || 'utf8';
|
||||
|
||||
this._buff = '';
|
||||
}
|
||||
|
||||
module.exports = ReadToEnd;
|
||||
util.inherits(ReadToEnd, stream.Transform);
|
||||
|
||||
ReadToEnd.prototype._transform = function(chunk, encoding, done) {
|
||||
this._buff += chunk.toString(this._rte_encoding);
|
||||
this.push(chunk);
|
||||
done();
|
||||
};
|
||||
|
||||
ReadToEnd.prototype._flush = function(done) {
|
||||
this.emit('complete', undefined, this._buff);
|
||||
done();
|
||||
};
|
||||
|
||||
ReadToEnd.readToEnd = function(stream, options, cb) {
|
||||
if (!cb) {
|
||||
cb = options;
|
||||
options = {};
|
||||
_transform(chunk, encoding, done) {
|
||||
this._buffer += chunk.toString(this._encoding);
|
||||
this.push(chunk);
|
||||
done();
|
||||
}
|
||||
|
||||
const dest = new ReadToEnd(options);
|
||||
_flush(done) {
|
||||
this.emit('complete', null, this._buffer);
|
||||
done();
|
||||
}
|
||||
|
||||
stream.pipe(dest);
|
||||
static readToEnd(stream, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
stream.on('error', function(err) {
|
||||
stream.unpipe(dest);
|
||||
cb(err);
|
||||
});
|
||||
const dest = new ReadToEnd(options);
|
||||
|
||||
dest.on('complete', cb);
|
||||
stream.pipe(dest);
|
||||
|
||||
dest.resume();
|
||||
stream.on('error', (err) => {
|
||||
stream.unpipe(dest);
|
||||
callback(err);
|
||||
});
|
||||
|
||||
return dest;
|
||||
};
|
||||
dest.on('complete', callback);
|
||||
dest.resume();
|
||||
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
+52
-9
@@ -1,4 +1,4 @@
|
||||
type X2jOptions = {
|
||||
export type X2jOptions = {
|
||||
/**
|
||||
* Preserve the order of tags in resulting JS object
|
||||
*
|
||||
@@ -30,9 +30,17 @@ type X2jOptions = {
|
||||
/**
|
||||
* Whether to ignore attributes when parsing
|
||||
*
|
||||
* When `true` - ignores all the attributes
|
||||
*
|
||||
* When `false` - parses all the attributes
|
||||
*
|
||||
* When `Array<string | RegExp>` - filters out attributes that match provided patterns
|
||||
*
|
||||
* When `Function` - calls the function for each attribute and filters out those for which the function returned `true`
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
ignoreAttributes?: boolean;
|
||||
ignoreAttributes?: boolean | (string | RegExp)[] | ((attrName: string, jPath: string) => boolean);
|
||||
|
||||
/**
|
||||
* Whether to remove namespace string from tag and attribute names
|
||||
@@ -202,16 +210,22 @@ type X2jOptions = {
|
||||
* Defaults to `(tagName, jPath, attrs) => tagName`
|
||||
*/
|
||||
updateTag?: (tagName: string, jPath: string, attrs: {[k: string]: string}) => string | boolean;
|
||||
|
||||
/**
|
||||
* If true, adds a Symbol to all object nodes, accessible by {@link XMLParser.getMetaDataSymbol} with
|
||||
* metadata about each the node in the XML file.
|
||||
*/
|
||||
captureMetaData?: boolean;
|
||||
};
|
||||
|
||||
type strnumOptions = {
|
||||
export type strnumOptions = {
|
||||
hex: boolean;
|
||||
leadingZeros: boolean,
|
||||
skipLike?: RegExp,
|
||||
eNotation?: boolean
|
||||
}
|
||||
|
||||
type validationOptions = {
|
||||
export type validationOptions = {
|
||||
/**
|
||||
* Whether to allow attributes without value
|
||||
*
|
||||
@@ -227,7 +241,7 @@ type validationOptions = {
|
||||
unpairedTags?: string[];
|
||||
};
|
||||
|
||||
type XmlBuilderOptions = {
|
||||
export type XmlBuilderOptions = {
|
||||
/**
|
||||
* Give a prefix to the attribute name in the resulting JS object
|
||||
*
|
||||
@@ -250,11 +264,19 @@ type XmlBuilderOptions = {
|
||||
textNodeName?: string;
|
||||
|
||||
/**
|
||||
* Whether to ignore attributes when parsing
|
||||
* Whether to ignore attributes when building
|
||||
*
|
||||
* When `true` - ignores all the attributes
|
||||
*
|
||||
* When `false` - builds all the attributes
|
||||
*
|
||||
* When `Array<string | RegExp>` - filters out attributes that match provided patterns
|
||||
*
|
||||
* When `Function` - calls the function for each attribute and filters out those for which the function returned `true`
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
ignoreAttributes?: boolean;
|
||||
ignoreAttributes?: boolean | (string | RegExp)[] | ((attrName: string, jPath: string) => boolean);
|
||||
|
||||
/**
|
||||
* Give a property name to set CDATA values to instead of merging to tag's text value
|
||||
@@ -373,7 +395,7 @@ type XmlBuilderOptions = {
|
||||
|
||||
type ESchema = string | object | Array<string|object>;
|
||||
|
||||
type ValidationError = {
|
||||
export type ValidationError = {
|
||||
err: {
|
||||
code: string;
|
||||
msg: string,
|
||||
@@ -391,6 +413,18 @@ export class XMLParser {
|
||||
* @param entityValue {string} Eg: '\r'
|
||||
*/
|
||||
addEntity(entityIdentifier: string, entityValue: string): void;
|
||||
|
||||
/**
|
||||
* Returns a Symbol that can be used to access the {@link XMLMetaData}
|
||||
* property on a node.
|
||||
*
|
||||
* If Symbol is not available in the environment, an ordinary property is used
|
||||
* and the name of the property is here returned.
|
||||
*
|
||||
* The XMLMetaData property is only present when {@link X2jOptions.captureMetaData}
|
||||
* is true in the options.
|
||||
*/
|
||||
static getMetaDataSymbol() : Symbol;
|
||||
}
|
||||
|
||||
export class XMLValidator{
|
||||
@@ -398,5 +432,14 @@ export class XMLValidator{
|
||||
}
|
||||
export class XMLBuilder {
|
||||
constructor(options?: XmlBuilderOptions);
|
||||
build(jObj: any): any;
|
||||
build(jObj: any): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This object is available on nodes via the symbol {@link XMLParser.getMetaDataSymbol}
|
||||
* when {@link X2jOptions.captureMetaData} is true.
|
||||
*/
|
||||
export interface XMLMetaData {
|
||||
/** The index, if available, of the character where the XML node began in the input stream. */
|
||||
startIndex?: number;
|
||||
}
|
||||
|
||||
+11
-8
@@ -1,11 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const validator = require('./validator');
|
||||
const XMLParser = require('./xmlparser/XMLParser');
|
||||
const XMLBuilder = require('./xmlbuilder/json2xml');
|
||||
import {validate} from './validator.js';
|
||||
import XMLParser from './xmlparser/XMLParser.js';
|
||||
import XMLBuilder from './xmlbuilder/json2xml.js';
|
||||
|
||||
module.exports = {
|
||||
XMLParser: XMLParser,
|
||||
XMLValidator: validator,
|
||||
XMLBuilder: XMLBuilder
|
||||
}
|
||||
const XMLValidator = {
|
||||
validate: validate
|
||||
}
|
||||
export {
|
||||
XMLParser,
|
||||
XMLValidator,
|
||||
XMLBuilder
|
||||
};
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
export default function getIgnoreAttributesFn(ignoreAttributes) {
|
||||
if (typeof ignoreAttributes === 'function') {
|
||||
return ignoreAttributes
|
||||
}
|
||||
if (Array.isArray(ignoreAttributes)) {
|
||||
return (attrName) => {
|
||||
for (const pattern of ignoreAttributes) {
|
||||
if (typeof pattern === 'string' && attrName === pattern) {
|
||||
return true
|
||||
}
|
||||
if (pattern instanceof RegExp && pattern.test(attrName)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return () => false
|
||||
}
|
||||
+14
-18
@@ -2,10 +2,10 @@
|
||||
|
||||
const nameStartChar = ':A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD';
|
||||
const nameChar = nameStartChar + '\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040';
|
||||
const nameRegexp = '[' + nameStartChar + '][' + nameChar + ']*'
|
||||
export const nameRegexp = '[' + nameStartChar + '][' + nameChar + ']*';
|
||||
const regexName = new RegExp('^' + nameRegexp + '$');
|
||||
|
||||
const getAllMatches = function(string, regex) {
|
||||
export function getAllMatches(string, regex) {
|
||||
const matches = [];
|
||||
let match = regex.exec(string);
|
||||
while (match) {
|
||||
@@ -19,27 +19,27 @@ const getAllMatches = function(string, regex) {
|
||||
match = regex.exec(string);
|
||||
}
|
||||
return matches;
|
||||
};
|
||||
}
|
||||
|
||||
const isName = function(string) {
|
||||
export const isName = function(string) {
|
||||
const match = regexName.exec(string);
|
||||
return !(match === null || typeof match === 'undefined');
|
||||
};
|
||||
}
|
||||
|
||||
exports.isExist = function(v) {
|
||||
export function isExist(v) {
|
||||
return typeof v !== 'undefined';
|
||||
};
|
||||
}
|
||||
|
||||
exports.isEmptyObject = function(obj) {
|
||||
export function isEmptyObject(obj) {
|
||||
return Object.keys(obj).length === 0;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all the properties of a into b.
|
||||
* @param {*} target
|
||||
* @param {*} a
|
||||
*/
|
||||
exports.merge = function(target, a, arrayMode) {
|
||||
export function merge(target, a, arrayMode) {
|
||||
if (a) {
|
||||
const keys = Object.keys(a); // will return an array of own properties
|
||||
const len = keys.length; //don't make it inline
|
||||
@@ -51,22 +51,18 @@ exports.merge = function(target, a, arrayMode) {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
/* exports.merge =function (b,a){
|
||||
return Object.assign(b,a);
|
||||
} */
|
||||
|
||||
exports.getValue = function(v) {
|
||||
export function getValue(v) {
|
||||
if (exports.isExist(v)) {
|
||||
return v;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// const fakeCall = function(a) {return a;};
|
||||
// const fakeCallNoReturn = function() {};
|
||||
|
||||
exports.isName = isName;
|
||||
exports.getAllMatches = getAllMatches;
|
||||
exports.nameRegexp = nameRegexp;
|
||||
// const fakeCallNoReturn = function() {};
|
||||
Generated
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
modules.export = {
|
||||
export default {
|
||||
"<" : "<", //tag start
|
||||
">" : ">", //tag end
|
||||
"/" : "/", //close tag
|
||||
Generated
Vendored
+3
-6
@@ -13,11 +13,10 @@ const htmlEntities = {
|
||||
"copyright" : { regex: /&(copy|#169);/g, val: "©" },
|
||||
"reg" : { regex: /&(reg|#174);/g, val: "®" },
|
||||
"inr" : { regex: /&(inr|#8377);/g, val: "₹" },
|
||||
"num_dec": { regex: /&#([0-9]{1,7});/g, val : (_, str) => String.fromCharCode(Number.parseInt(str, 10)) },
|
||||
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val : (_, str) => String.fromCharCode(Number.parseInt(str, 16)) },
|
||||
"num_dec": { regex: /&#([0-9]{1,7});/g, val : (_, str) => String.fromCodePoint(Number.parseInt(str, 10)) },
|
||||
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val : (_, str) => String.fromCodePoint(Number.parseInt(str, 16)) },
|
||||
};
|
||||
|
||||
class EntitiesParser{
|
||||
export default class EntitiesParser{
|
||||
constructor(replaceHtmlEntities) {
|
||||
this.replaceHtmlEntities = replaceHtmlEntities;
|
||||
this.docTypeEntities = {};
|
||||
@@ -103,5 +102,3 @@ function validateEntityName(name){
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
module.exports = EntitiesParser;
|
||||
Generated
Vendored
+3
-6
@@ -1,7 +1,7 @@
|
||||
|
||||
const JsObjOutputBuilder = require("./OutputBuilders/JsObjBuilder");
|
||||
import {JsObjOutputBuilder} from './OutputBuilders/JsObjBuilder.js';
|
||||
|
||||
const defaultOptions = {
|
||||
export const defaultOptions = {
|
||||
preserveOrder: false,
|
||||
removeNSPrefix: false, // remove NS from tag name or attribute name if true
|
||||
//ignoreRootElement : false,
|
||||
@@ -35,7 +35,7 @@ const defaultOptions = {
|
||||
OutputBuilder: new JsObjOutputBuilder(),
|
||||
};
|
||||
|
||||
const buildOptions = function(options) {
|
||||
export const buildOptions = function(options) {
|
||||
const finalOptions = { ... defaultOptions};
|
||||
copyProperties(finalOptions,options)
|
||||
return finalOptions;
|
||||
@@ -59,6 +59,3 @@ function copyProperties(target, source) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.buildOptions = buildOptions;
|
||||
exports.defaultOptions = defaultOptions;
|
||||
+1
-3
@@ -1,4 +1,4 @@
|
||||
class BaseOutputBuilder{
|
||||
export default class BaseOutputBuilder{
|
||||
constructor(){
|
||||
// this.attributes = {};
|
||||
}
|
||||
@@ -67,5 +67,3 @@ class BaseOutputBuilder{
|
||||
this.attributes = {}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BaseOutputBuilder;
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
const {buildOptions,registerCommonValueParsers} = require("./ParserOptionsBuilder");
|
||||
import {buildOptions,registerCommonValueParsers} from './ParserOptionsBuilder.js';
|
||||
|
||||
class OutputBuilder{
|
||||
export default class OutputBuilder{
|
||||
constructor(options){
|
||||
this.options = buildOptions(options);
|
||||
this.registeredParsers = registerCommonValueParsers(this.options);
|
||||
@@ -16,7 +16,7 @@ class OutputBuilder{
|
||||
}
|
||||
|
||||
const rootName = '!js_arr';
|
||||
const BaseOutputBuilder = require("./BaseOutputBuilder");
|
||||
import BaseOutputBuilder from './BaseOutputBuilder.js';
|
||||
|
||||
class JsArrBuilder extends BaseOutputBuilder{
|
||||
|
||||
+3
-5
@@ -1,6 +1,6 @@
|
||||
const {buildOptions,registerCommonValueParsers} = require("./ParserOptionsBuilder");
|
||||
import {buildOptions,registerCommonValueParsers} from"./ParserOptionsBuilder";
|
||||
|
||||
class OutputBuilder{
|
||||
export default class OutputBuilder{
|
||||
constructor(options){
|
||||
this.options = buildOptions(options);
|
||||
this.registeredParsers = registerCommonValueParsers(this.options);
|
||||
@@ -15,7 +15,7 @@ class OutputBuilder{
|
||||
}
|
||||
}
|
||||
|
||||
const BaseOutputBuilder = require("./BaseOutputBuilder");
|
||||
import BaseOutputBuilder from "./BaseOutputBuilder.js";
|
||||
const rootName = '^';
|
||||
|
||||
class JsMinArrBuilder extends BaseOutputBuilder{
|
||||
@@ -98,5 +98,3 @@ class JsMinArrBuilder extends BaseOutputBuilder{
|
||||
return this.root[rootName];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OutputBuilder;
|
||||
+3
-5
@@ -1,8 +1,8 @@
|
||||
|
||||
|
||||
const {buildOptions,registerCommonValueParsers} = require("./ParserOptionsBuilder");
|
||||
import {buildOptions,registerCommonValueParsers} from './ParserOptionsBuilder.js';
|
||||
|
||||
class OutputBuilder{
|
||||
export default class OutputBuilder{
|
||||
constructor(builderOptions){
|
||||
this.options = buildOptions(builderOptions);
|
||||
this.registeredParsers = registerCommonValueParsers(this.options);
|
||||
@@ -17,7 +17,7 @@ class OutputBuilder{
|
||||
}
|
||||
}
|
||||
|
||||
const BaseOutputBuilder = require("./BaseOutputBuilder");
|
||||
import BaseOutputBuilder from './BaseOutputBuilder.js';
|
||||
const rootName = '^';
|
||||
|
||||
class JsObjBuilder extends BaseOutputBuilder{
|
||||
@@ -152,5 +152,3 @@ class JsObjBuilder extends BaseOutputBuilder{
|
||||
function isEmpty(obj) {
|
||||
return Object.keys(obj).length === 0;
|
||||
}
|
||||
|
||||
module.exports = OutputBuilder;
|
||||
+6
-11
@@ -1,7 +1,7 @@
|
||||
const trimParser = require("../valueParsers/trim")
|
||||
const booleanParser = require("../valueParsers/booleanParser")
|
||||
const currencyParser = require("../valueParsers/currency")
|
||||
const numberParser = require("../valueParsers/number")
|
||||
import trimParser from "../valueParsers/trim";
|
||||
import booleanParser from "../valueParsers/booleanParser";
|
||||
import currencyParser from "../valueParsers/currency";
|
||||
import numberParser from "../valueParsers/number";
|
||||
|
||||
const defaultOptions={
|
||||
nameFor:{
|
||||
@@ -44,7 +44,7 @@ const defaultOptions={
|
||||
const withJoin = ["trim","join", /*"entities",*/"number","boolean","currency"/*, "date"*/]
|
||||
const withoutJoin = ["trim", /*"entities",*/"number","boolean","currency"/*, "date"*/]
|
||||
|
||||
function buildOptions(options){
|
||||
export function buildOptions(options){
|
||||
//clone
|
||||
const finalOptions = { ... defaultOptions};
|
||||
|
||||
@@ -78,7 +78,7 @@ function copyProperties(target, source) {
|
||||
}
|
||||
}
|
||||
|
||||
function registerCommonValueParsers(options){
|
||||
export function registerCommonValueParsers(options){
|
||||
return {
|
||||
"trim": new trimParser(),
|
||||
// "join": this.entityParser.parse,
|
||||
@@ -92,8 +92,3 @@ function registerCommonValueParsers(options){
|
||||
// "date": this.entityParser.parse,
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildOptions : buildOptions,
|
||||
registerCommonValueParsers: registerCommonValueParsers
|
||||
}
|
||||
Generated
Vendored
Generated
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
class TagPath{
|
||||
export default class TagPath{
|
||||
constructor(pathStr){
|
||||
let text = "";
|
||||
let tName = "";
|
||||
Generated
Vendored
+3
-5
@@ -1,6 +1,6 @@
|
||||
const TagPath = require("./TagPath");
|
||||
import {TagPath} from './TagPath.js';
|
||||
|
||||
class TagPathMatcher{
|
||||
export default class TagPathMatcher{
|
||||
constructor(stack,node){
|
||||
this.stack = stack;
|
||||
this.node= node;
|
||||
@@ -10,6 +10,4 @@ class TagPathMatcher{
|
||||
const tagPath = new TagPath(path);
|
||||
return tagPath.match(this.stack, this.node);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TagPathMatcher;
|
||||
}
|
||||
Generated
Vendored
+3
-5
@@ -1,7 +1,7 @@
|
||||
const { buildOptions} = require("./OptionsBuilder");
|
||||
const Xml2JsParser = require("./Xml2JsParser");
|
||||
import { buildOptions} from './OptionsBuilder.js';
|
||||
import Xml2JsParser from './Xml2JsParser.js';
|
||||
|
||||
class XMLParser{
|
||||
export default class XMLParser{
|
||||
|
||||
constructor(options){
|
||||
this.externalEntities = {};
|
||||
@@ -81,5 +81,3 @@ function isStream(stream){
|
||||
if(stream && typeof stream.read === "function" && typeof stream.on === "function" && typeof stream.readableEnded === "boolean") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = XMLParser;
|
||||
Generated
Vendored
+8
-10
@@ -1,10 +1,10 @@
|
||||
const StringSource = require("./inputSource/StringSource");
|
||||
const BufferSource = require("./inputSource/BufferSource");
|
||||
const {readTagExp,readClosingTagName} = require("./XmlPartReader");
|
||||
const {readComment, readCdata,readDocType,readPiTag} = require("./XmlSpecialTagsReader");
|
||||
const TagPath = require("./TagPath");
|
||||
const TagPathMatcher = require("./TagPathMatcher");
|
||||
const EntitiesParser = require('./EntitiesParser');
|
||||
import StringSource from './inputSource/StringSource.js';
|
||||
import BufferSource from './inputSource/BufferSource.js';
|
||||
import {readTagExp,readClosingTagName} from './XmlPartReader.js';
|
||||
import {readComment, readCdata,readDocType,readPiTag} from './XmlSpecialTagsReader.js';
|
||||
import TagPath from './TagPath.js';
|
||||
import TagPathMatcher from './TagPathMatcher.js';
|
||||
import EntitiesParser from './EntitiesParser.js';
|
||||
|
||||
//To hold the data of current tag
|
||||
//This is usually used to compare jpath expression against current tag
|
||||
@@ -16,7 +16,7 @@ class TagDetail{
|
||||
}
|
||||
}
|
||||
|
||||
class Xml2JsParser {
|
||||
export default class Xml2JsParser {
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
|
||||
@@ -233,5 +233,3 @@ function resolveNameSpace(name, removeNSPrefix) {
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
module.exports = Xml2JsParser;
|
||||
Generated
Vendored
+24
-26
@@ -2,17 +2,17 @@
|
||||
|
||||
/**
|
||||
* find paired tag for a stop node
|
||||
* @param {string} xmlDoc
|
||||
* @param {string} tagName
|
||||
* @param {string} xmlDoc
|
||||
* @param {string} tagName
|
||||
* @param {number} i : start index
|
||||
*/
|
||||
function readStopNode(xmlDoc, tagName, i){
|
||||
export function readStopNode(xmlDoc, tagName, i){
|
||||
const startIndex = i;
|
||||
// Starting at 1 since we already have an open tag
|
||||
let openTagCount = 1;
|
||||
|
||||
|
||||
for (; i < xmlDoc.length; i++) {
|
||||
if( xmlDoc[i] === "<"){
|
||||
if( xmlDoc[i] === "<"){
|
||||
if (xmlDoc[i+1] === "/") {//close tag
|
||||
const closeIndex = findSubStrIndex(xmlDoc, ">", i, `${tagName} is not closed`);
|
||||
let closeTagName = xmlDoc.substring(i+2,closeIndex).trim();
|
||||
@@ -26,18 +26,18 @@ function readStopNode(xmlDoc, tagName, i){
|
||||
}
|
||||
}
|
||||
i=closeIndex;
|
||||
} else if(xmlDoc[i+1] === '?') {
|
||||
} else if(xmlDoc[i+1] === '?') {
|
||||
const closeIndex = findSubStrIndex(xmlDoc, "?>", i+1, "StopNode is not closed.")
|
||||
i=closeIndex;
|
||||
} else if(xmlDoc.substr(i + 1, 3) === '!--') {
|
||||
} else if(xmlDoc.substr(i + 1, 3) === '!--') {
|
||||
const closeIndex = findSubStrIndex(xmlDoc, "-->", i+3, "StopNode is not closed.")
|
||||
i=closeIndex;
|
||||
} else if(xmlDoc.substr(i + 1, 2) === '![') {
|
||||
} else if(xmlDoc.substr(i + 1, 2) === '![') {
|
||||
const closeIndex = findSubStrIndex(xmlDoc, "]]>", i, "StopNode is not closed.") - 2;
|
||||
i=closeIndex;
|
||||
} else {
|
||||
const tagData = readTagExp(xmlDoc, i, '>')
|
||||
|
||||
|
||||
if (tagData) {
|
||||
const openTagName = tagData && tagData.tagName;
|
||||
if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length-1] !== "/") {
|
||||
@@ -52,10 +52,10 @@ function readStopNode(xmlDoc, tagName, i){
|
||||
|
||||
/**
|
||||
* Read closing tag name
|
||||
* @param {Source} source
|
||||
* @param {Source} source
|
||||
* @returns tag name
|
||||
*/
|
||||
function readClosingTagName(source){
|
||||
export function readClosingTagName(source){
|
||||
let text = ""; //temporary data
|
||||
while(source.canRead()){
|
||||
let ch = source.readCh();
|
||||
@@ -73,11 +73,11 @@ function readClosingTagName(source){
|
||||
* This function can be used to read normal tag, pi tag.
|
||||
* This function can't be used to read comment, CDATA, DOCTYPE.
|
||||
* Eg <tag attr = ' some"' attr= ">" bool>
|
||||
* @param {string} xmlDoc
|
||||
* @param {string} xmlDoc
|
||||
* @param {number} startIndex starting index
|
||||
* @returns tag expression includes tag name & attribute string
|
||||
* @returns tag expression includes tag name & attribute string
|
||||
*/
|
||||
function readTagExp(parser) {
|
||||
export function readTagExp(parser) {
|
||||
let inSingleQuotes = false;
|
||||
let inDoubleQuotes = false;
|
||||
let i;
|
||||
@@ -100,14 +100,14 @@ function readTagExp(parser) {
|
||||
if(inSingleQuotes || inDoubleQuotes){
|
||||
throw new Error("Invalid attribute expression. Quote is not properly closed");
|
||||
}else if(!EOE) throw new Error("Unexpected closing of source. Waiting for '>'");
|
||||
|
||||
|
||||
|
||||
|
||||
const exp = parser.source.readStr(i);
|
||||
parser.source.updateBufferBoundary(i + 1);
|
||||
return buildTagExpObj(exp, parser)
|
||||
}
|
||||
|
||||
function readPiExp(parser) {
|
||||
export function readPiExp(parser) {
|
||||
let inSingleQuotes = false;
|
||||
let inDoubleQuotes = false;
|
||||
let i;
|
||||
@@ -133,7 +133,7 @@ function readPiExp(parser) {
|
||||
if(inSingleQuotes || inDoubleQuotes){
|
||||
throw new Error("Invalid attribute expression. Quote is not properly closed in PI tag expression");
|
||||
}else if(!EOE) throw new Error("Unexpected closing of source. Waiting for '?>'");
|
||||
|
||||
|
||||
if(!parser.options.attributes.ignore){
|
||||
//TODO: use regex to verify attributes if not set to ignore
|
||||
}
|
||||
@@ -150,7 +150,11 @@ function buildTagExpObj(exp, parser){
|
||||
};
|
||||
let attrsExp = "";
|
||||
|
||||
if(exp[exp.length -1] === "/") tagExp.selfClosing = true;
|
||||
// Check for self-closing tag before setting the name
|
||||
if(exp[exp.length -1] === "/") {
|
||||
tagExp.selfClosing = true;
|
||||
exp = exp.slice(0, -1); // Remove the trailing slash
|
||||
}
|
||||
|
||||
//separate tag name
|
||||
let i = 0;
|
||||
@@ -182,7 +186,7 @@ function parseAttributesExp(attrStr, parser) {
|
||||
for (let i = 0; i < len; i++) {
|
||||
let attrName = parser.processAttrName(matches[i][1]);
|
||||
let attrVal = parser.replaceEntities(matches[i][4] || true);
|
||||
|
||||
|
||||
parser.outputBuilder.addAttribute(attrName, attrVal);
|
||||
}
|
||||
}
|
||||
@@ -204,9 +208,3 @@ const getAllMatches = function(string, regex) {
|
||||
return matches;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
readStopNode: readStopNode,
|
||||
readClosingTagName: readClosingTagName,
|
||||
readTagExp: readTagExp,
|
||||
readPiExp: readPiExp,
|
||||
}
|
||||
Generated
Vendored
+5
-12
@@ -1,6 +1,6 @@
|
||||
const {readPiExp} = require("./XmlPartReader");
|
||||
import {readPiExp} from './XmlPartReader.js';
|
||||
|
||||
function readCdata(parser){
|
||||
export function readCdata(parser){
|
||||
//<![ are already read till this point
|
||||
let str = parser.source.readStr(6); //CDATA[
|
||||
parser.source.updateBufferBoundary(6);
|
||||
@@ -10,7 +10,7 @@ function readCdata(parser){
|
||||
let text = parser.source.readUpto("]]>");
|
||||
parser.outputBuilder.addCdata(text);
|
||||
}
|
||||
function readPiTag(parser){
|
||||
export function readPiTag(parser){
|
||||
//<? are already read till this point
|
||||
let tagExp = readPiExp(parser, "?>");
|
||||
if(!tagExp) throw new Error("Invalid Pi Tag expression.");
|
||||
@@ -22,7 +22,7 @@ function readPiTag(parser){
|
||||
}
|
||||
}
|
||||
|
||||
function readComment(parser){
|
||||
export function readComment(parser){
|
||||
//<!- are already read till this point
|
||||
let ch = parser.source.readCh();
|
||||
if(ch !== "-") throw new Error(`Invalid comment expression at ${parser.source.line}:${parser.source.cols}`);
|
||||
@@ -36,7 +36,7 @@ const DOCTYPE_tags = {
|
||||
"AT":/^TLIST\s+[^\s]+\s+[^\s]+\s+[^\s]+\s+[^\s]+\s+$/m,
|
||||
"NO":/^TATION.+$/m
|
||||
}
|
||||
function readDocType(parser){
|
||||
export function readDocType(parser){
|
||||
//<!D are already read till this point
|
||||
let str = parser.source.readStr(6); //OCTYPE
|
||||
parser.source.updateBufferBoundary(6);
|
||||
@@ -109,10 +109,3 @@ function registerEntity(parser){
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
readCdata: readCdata,
|
||||
readComment:readComment,
|
||||
readDocType:readDocType,
|
||||
readPiTag:readPiTag
|
||||
}
|
||||
Generated
Vendored
+1
-3
@@ -2,7 +2,7 @@ const Constants = {
|
||||
space: 32,
|
||||
tab: 9
|
||||
}
|
||||
class BufferSource{
|
||||
export default class BufferSource{
|
||||
constructor(bytesArr){
|
||||
this.line = 1;
|
||||
this.cols = 0;
|
||||
@@ -114,5 +114,3 @@ readUptoCloseTag(stopStr) { //stopStr: "</tagname"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = BufferSource;
|
||||
Generated
Vendored
+1
-3
@@ -1,7 +1,7 @@
|
||||
const whiteSpaces = [" ", "\n", "\t"];
|
||||
|
||||
|
||||
class StringSource{
|
||||
export default class StringSource{
|
||||
constructor(str){
|
||||
this.line = 1;
|
||||
this.cols = 0;
|
||||
@@ -119,5 +119,3 @@ class StringSource{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = StringSource;
|
||||
Generated
Vendored
+3
-5
@@ -13,11 +13,11 @@ const htmlEntities = {
|
||||
"copyright" : { regex: /&(copy|#169);/g, val: "©" },
|
||||
"reg" : { regex: /&(reg|#174);/g, val: "®" },
|
||||
"inr" : { regex: /&(inr|#8377);/g, val: "₹" },
|
||||
"num_dec": { regex: /&#([0-9]{1,7});/g, val : (_, str) => String.fromCharCode(Number.parseInt(str, 10)) },
|
||||
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val : (_, str) => String.fromCharCode(Number.parseInt(str, 16)) },
|
||||
"num_dec": { regex: /&#([0-9]{1,7});/g, val : (_, str) => String.fromCodePoint(Number.parseInt(str, 10)) },
|
||||
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val : (_, str) => String.fromCodePoint(Number.parseInt(str, 16)) },
|
||||
};
|
||||
|
||||
class EntitiesParser{
|
||||
export default class EntitiesParser{
|
||||
constructor(replaceHtmlEntities) {
|
||||
this.replaceHtmlEntities = replaceHtmlEntities;
|
||||
this.docTypeEntities = {};
|
||||
@@ -103,5 +103,3 @@ function validateEntityName(name){
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
module.exports = EntitiesParser;
|
||||
+1
-2
@@ -1,4 +1,4 @@
|
||||
class boolParser{
|
||||
export default class boolParser{
|
||||
constructor(trueList, falseList){
|
||||
if(trueList)
|
||||
this.trueList = trueList;
|
||||
@@ -20,4 +20,3 @@ class boolParser{
|
||||
return val;
|
||||
}
|
||||
}
|
||||
module.exports = boolParser;
|
||||
+1
-2
@@ -1,4 +1,4 @@
|
||||
function boolParserExt(val){
|
||||
export default function boolParserExt(val){
|
||||
if(isArray(val)){
|
||||
for (let i = 0; i < val.length; i++) {
|
||||
val[i] = parse(val[i])
|
||||
@@ -17,4 +17,3 @@ function parse(val){
|
||||
}
|
||||
return val;
|
||||
}
|
||||
module.exports = boolParserExt;
|
||||
Generated
Vendored
+1
-3
@@ -16,7 +16,7 @@ const symbol = "(?:\$|€|¥|₹)?";
|
||||
|
||||
const currencyCheckRegex = /^\s*(?:-|\+)?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d{1,2})?\s*(?:\$|€|¥|₹)?\s*$/u;
|
||||
|
||||
class CurrencyParser{
|
||||
export default class CurrencyParser{
|
||||
constructor(options){
|
||||
this.options = options || defaultOptions;
|
||||
}
|
||||
@@ -36,5 +36,3 @@ class CurrencyParser{
|
||||
}
|
||||
}
|
||||
CurrencyParser.defaultOptions = defaultOptions;
|
||||
|
||||
module.exports = CurrencyParser;
|
||||
Generated
Vendored
+1
-2
@@ -4,11 +4,10 @@
|
||||
* @param {string} by
|
||||
* @returns
|
||||
*/
|
||||
function join(val, by=" "){
|
||||
export default function join(val, by=" "){
|
||||
if(isArray(val)){
|
||||
val.join(by)
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
module.exports = join;
|
||||
Generated
Vendored
+2
-4
@@ -1,7 +1,7 @@
|
||||
const toNumber = require("strnum");
|
||||
import toNumber from "strnum";
|
||||
|
||||
|
||||
class numParser{
|
||||
export default class numParser{
|
||||
constructor(options){
|
||||
this.options = options;
|
||||
}
|
||||
@@ -12,5 +12,3 @@ class numParser{
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = numParser;
|
||||
Generated
Vendored
+1
-3
@@ -1,8 +1,6 @@
|
||||
class trimmer{
|
||||
export default class trimmer{
|
||||
parse(val){
|
||||
if(typeof val === "string") return val.trim();
|
||||
else return val;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = trimmer;
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const util = require('./util');
|
||||
import {getAllMatches, isName} from './util.js';
|
||||
|
||||
const defaultOptions = {
|
||||
allowBooleanAttributes: false, //A tag can have attributes without any value
|
||||
@@ -8,7 +8,7 @@ const defaultOptions = {
|
||||
};
|
||||
|
||||
//const tagsPattern = new RegExp("<\\/?([\\w:\\-_\.]+)\\s*\/?>","g");
|
||||
exports.validate = function (xmlData, options) {
|
||||
export function validate(xmlData, options) {
|
||||
options = Object.assign({}, defaultOptions, options);
|
||||
|
||||
//xmlData = xmlData.replace(/(\r\n|\n|\r)/gm,"");//make it single line
|
||||
@@ -321,7 +321,7 @@ function validateAttributeString(attrStr, options) {
|
||||
|
||||
//if(attrStr.trim().length === 0) return true; //empty string
|
||||
|
||||
const matches = util.getAllMatches(attrStr, validAttrStrRegxp);
|
||||
const matches = getAllMatches(attrStr, validAttrStrRegxp);
|
||||
const attrNames = {};
|
||||
|
||||
for (let i = 0; i < matches.length; i++) {
|
||||
@@ -399,13 +399,13 @@ function getErrorObject(code, message, lineNumber) {
|
||||
}
|
||||
|
||||
function validateAttrName(attrName) {
|
||||
return util.isName(attrName);
|
||||
return isName(attrName);
|
||||
}
|
||||
|
||||
// const startsWithXML = /^xml/i;
|
||||
|
||||
function validateTagName(tagname) {
|
||||
return util.isName(tagname) /* && !tagname.match(startsWithXML) */;
|
||||
return isName(tagname) /* && !tagname.match(startsWithXML) */;
|
||||
}
|
||||
|
||||
//this function returns the line number for the character at the given index
|
||||
|
||||
+17
-13
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
//parse Empty Node as self closing node
|
||||
const buildFromOrderedJs = require('./orderedJs2Xml');
|
||||
import buildFromOrderedJs from './orderedJs2Xml.js';
|
||||
import getIgnoreAttributesFn from "../ignoreAttributes.js";
|
||||
|
||||
const defaultOptions = {
|
||||
attributeNamePrefix: '@_',
|
||||
@@ -36,13 +37,14 @@ const defaultOptions = {
|
||||
oneListGroup: false
|
||||
};
|
||||
|
||||
function Builder(options) {
|
||||
export default function Builder(options) {
|
||||
this.options = Object.assign({}, defaultOptions, options);
|
||||
if (this.options.ignoreAttributes || this.options.attributesGroupName) {
|
||||
if (this.options.ignoreAttributes === true || this.options.attributesGroupName) {
|
||||
this.isAttribute = function(/*a*/) {
|
||||
return false;
|
||||
};
|
||||
} else {
|
||||
this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes)
|
||||
this.attrPrefixLen = this.options.attributeNamePrefix.length;
|
||||
this.isAttribute = isAttribute;
|
||||
}
|
||||
@@ -71,13 +73,14 @@ Builder.prototype.build = function(jObj) {
|
||||
[this.options.arrayNodeName] : jObj
|
||||
}
|
||||
}
|
||||
return this.j2x(jObj, 0).val;
|
||||
return this.j2x(jObj, 0, []).val;
|
||||
}
|
||||
};
|
||||
|
||||
Builder.prototype.j2x = function(jObj, level) {
|
||||
Builder.prototype.j2x = function(jObj, level, ajPath) {
|
||||
let attrStr = '';
|
||||
let val = '';
|
||||
const jPath = ajPath.join('.')
|
||||
for (let key in jObj) {
|
||||
if(!Object.prototype.hasOwnProperty.call(jObj, key)) continue;
|
||||
if (typeof jObj[key] === 'undefined') {
|
||||
@@ -89,6 +92,8 @@ Builder.prototype.j2x = function(jObj, level) {
|
||||
// null attribute should be ignored by the attribute list, but should not cause the tag closing
|
||||
if (this.isAttribute(key)) {
|
||||
val += '';
|
||||
} else if (key === this.options.cdataPropName) {
|
||||
val += '';
|
||||
} else if (key[0] === '?') {
|
||||
val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
|
||||
} else {
|
||||
@@ -100,9 +105,9 @@ Builder.prototype.j2x = function(jObj, level) {
|
||||
} else if (typeof jObj[key] !== 'object') {
|
||||
//premitive type
|
||||
const attr = this.isAttribute(key);
|
||||
if (attr) {
|
||||
if (attr && !this.ignoreAttributesFn(attr, jPath)) {
|
||||
attrStr += this.buildAttrPairStr(attr, '' + jObj[key]);
|
||||
}else {
|
||||
} else if (!attr) {
|
||||
//tag value
|
||||
if (key === this.options.textNodeName) {
|
||||
let newval = this.options.tagValueProcessor(key, '' + jObj[key]);
|
||||
@@ -126,13 +131,13 @@ Builder.prototype.j2x = function(jObj, level) {
|
||||
// val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
|
||||
} else if (typeof item === 'object') {
|
||||
if(this.options.oneListGroup){
|
||||
const result = this.j2x(item, level + 1);
|
||||
const result = this.j2x(item, level + 1, ajPath.concat(key));
|
||||
listTagVal += result.val;
|
||||
if (this.options.attributesGroupName && item.hasOwnProperty(this.options.attributesGroupName)) {
|
||||
listTagAttr += result.attrStr
|
||||
}
|
||||
}else{
|
||||
listTagVal += this.processTextOrObjNode(item, key, level)
|
||||
listTagVal += this.processTextOrObjNode(item, key, level, ajPath)
|
||||
}
|
||||
} else {
|
||||
if (this.options.oneListGroup) {
|
||||
@@ -157,7 +162,7 @@ Builder.prototype.j2x = function(jObj, level) {
|
||||
attrStr += this.buildAttrPairStr(Ks[j], '' + jObj[key][Ks[j]]);
|
||||
}
|
||||
} else {
|
||||
val += this.processTextOrObjNode(jObj[key], key, level)
|
||||
val += this.processTextOrObjNode(jObj[key], key, level, ajPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,8 +177,8 @@ Builder.prototype.buildAttrPairStr = function(attrName, val){
|
||||
} else return ' ' + attrName + '="' + val + '"';
|
||||
}
|
||||
|
||||
function processTextOrObjNode (object, key, level) {
|
||||
const result = this.j2x(object, level + 1);
|
||||
function processTextOrObjNode (object, key, level, ajPath) {
|
||||
const result = this.j2x(object, level + 1, ajPath.concat(key));
|
||||
if (object[this.options.textNodeName] !== undefined && Object.keys(object).length === 1) {
|
||||
return this.buildTextValNode(object[this.options.textNodeName], key, result.attrStr, level);
|
||||
} else {
|
||||
@@ -278,4 +283,3 @@ function isAttribute(name /*, options*/) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Builder;
|
||||
|
||||
+1
-2
@@ -6,7 +6,7 @@ const EOL = "\n";
|
||||
* @param {any} options
|
||||
* @returns
|
||||
*/
|
||||
function toXml(jArray, options) {
|
||||
export default function toXml(jArray, options) {
|
||||
let indentation = "";
|
||||
if (options.format && options.indentBy.length > 0) {
|
||||
indentation = EOL;
|
||||
@@ -132,4 +132,3 @@ function replaceEntitiesValue(textValue, options) {
|
||||
}
|
||||
return textValue;
|
||||
}
|
||||
module.exports = toXml;
|
||||
|
||||
+290
-73
@@ -1,7 +1,7 @@
|
||||
const util = require('../util');
|
||||
import {isName} from '../util.js';
|
||||
|
||||
//TODO: handle comments
|
||||
function readDocType(xmlData, i){
|
||||
export default function readDocType(xmlData, i){
|
||||
|
||||
const entities = {};
|
||||
if( xmlData[i + 3] === 'O' &&
|
||||
@@ -17,20 +17,30 @@ function readDocType(xmlData, i){
|
||||
let exp = "";
|
||||
for(;i<xmlData.length;i++){
|
||||
if (xmlData[i] === '<' && !comment) { //Determine the tag type
|
||||
if( hasBody && isEntity(xmlData, i)){
|
||||
if( hasBody && hasSeq(xmlData, "!ENTITY",i)){
|
||||
i += 7;
|
||||
let entityName, val;
|
||||
[entityName, val,i] = readEntityExp(xmlData,i+1);
|
||||
if(val.indexOf("&") === -1) //Parameter entities are not supported
|
||||
entities[ validateEntityName(entityName) ] = {
|
||||
entities[ entityName ] = {
|
||||
regx : RegExp( `&${entityName};`,"g"),
|
||||
val: val
|
||||
};
|
||||
}
|
||||
else if( hasBody && isElement(xmlData, i)) i += 8;//Not supported
|
||||
else if( hasBody && isAttlist(xmlData, i)) i += 8;//Not supported
|
||||
else if( hasBody && isNotation(xmlData, i)) i += 9;//Not supported
|
||||
else if( isComment) comment = true;
|
||||
else throw new Error("Invalid DOCTYPE");
|
||||
else if( hasBody && hasSeq(xmlData, "!ELEMENT",i)) {
|
||||
i += 8;//Not supported
|
||||
const {index} = readElementExp(xmlData,i+1);
|
||||
i = index;
|
||||
}else if( hasBody && hasSeq(xmlData, "!ATTLIST",i)){
|
||||
i += 8;//Not supported
|
||||
// const {index} = readAttlistExp(xmlData,i+1);
|
||||
// i = index;
|
||||
}else if( hasBody && hasSeq(xmlData, "!NOTATION",i)) {
|
||||
i += 9;//Not supported
|
||||
const {index} = readNotationExp(xmlData,i+1);
|
||||
i = index;
|
||||
}else if( hasSeq(xmlData, "!--",i) ) comment = true;
|
||||
else throw new Error(`Invalid DOCTYPE`);
|
||||
|
||||
angleBracketsCount++;
|
||||
exp = "";
|
||||
@@ -61,7 +71,14 @@ function readDocType(xmlData, i){
|
||||
return {entities, i};
|
||||
}
|
||||
|
||||
function readEntityExp(xmlData,i){
|
||||
const skipWhitespace = (data, index) => {
|
||||
while (index < data.length && /\s/.test(data[index])) {
|
||||
index++;
|
||||
}
|
||||
return index;
|
||||
};
|
||||
|
||||
function readEntityExp(xmlData, i) {
|
||||
//External entities are not supported
|
||||
// <!ENTITY ext SYSTEM "http://normal-website.com" >
|
||||
|
||||
@@ -70,83 +87,283 @@ function readEntityExp(xmlData,i){
|
||||
|
||||
//Internal entities are supported
|
||||
// <!ENTITY entityname "replacement text">
|
||||
|
||||
//read EntityName
|
||||
|
||||
// Skip leading whitespace after <!ENTITY
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read entity name
|
||||
let entityName = "";
|
||||
for (; i < xmlData.length && (xmlData[i] !== "'" && xmlData[i] !== '"' ); i++) {
|
||||
// if(xmlData[i] === " ") continue;
|
||||
// else
|
||||
while (i < xmlData.length && !/\s/.test(xmlData[i]) && xmlData[i] !== '"' && xmlData[i] !== "'") {
|
||||
entityName += xmlData[i];
|
||||
i++;
|
||||
}
|
||||
entityName = entityName.trim();
|
||||
if(entityName.indexOf(" ") !== -1) throw new Error("External entites are not supported");
|
||||
validateEntityName(entityName);
|
||||
|
||||
//read Entity Value
|
||||
const startChar = xmlData[i++];
|
||||
let val = ""
|
||||
for (; i < xmlData.length && xmlData[i] !== startChar ; i++) {
|
||||
val += xmlData[i];
|
||||
// Skip whitespace after entity name
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Check for unsupported constructs (external entities or parameter entities)
|
||||
if (xmlData.substring(i, i + 6).toUpperCase() === "SYSTEM") {
|
||||
throw new Error("External entities are not supported");
|
||||
}else if (xmlData[i] === "%") {
|
||||
throw new Error("Parameter entities are not supported");
|
||||
}
|
||||
return [entityName, val, i];
|
||||
|
||||
// Read entity value (internal entity)
|
||||
let entityValue = "";
|
||||
[i, entityValue] = readIdentifierVal(xmlData, i, "entity");
|
||||
i--;
|
||||
return [entityName, entityValue, i ];
|
||||
}
|
||||
|
||||
function isComment(xmlData, i){
|
||||
if(xmlData[i+1] === '!' &&
|
||||
xmlData[i+2] === '-' &&
|
||||
xmlData[i+3] === '-') return true
|
||||
return false
|
||||
}
|
||||
function isEntity(xmlData, i){
|
||||
if(xmlData[i+1] === '!' &&
|
||||
xmlData[i+2] === 'E' &&
|
||||
xmlData[i+3] === 'N' &&
|
||||
xmlData[i+4] === 'T' &&
|
||||
xmlData[i+5] === 'I' &&
|
||||
xmlData[i+6] === 'T' &&
|
||||
xmlData[i+7] === 'Y') return true
|
||||
return false
|
||||
}
|
||||
function isElement(xmlData, i){
|
||||
if(xmlData[i+1] === '!' &&
|
||||
xmlData[i+2] === 'E' &&
|
||||
xmlData[i+3] === 'L' &&
|
||||
xmlData[i+4] === 'E' &&
|
||||
xmlData[i+5] === 'M' &&
|
||||
xmlData[i+6] === 'E' &&
|
||||
xmlData[i+7] === 'N' &&
|
||||
xmlData[i+8] === 'T') return true
|
||||
return false
|
||||
function readNotationExp(xmlData, i) {
|
||||
// Skip leading whitespace after <!NOTATION
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read notation name
|
||||
let notationName = "";
|
||||
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
||||
notationName += xmlData[i];
|
||||
i++;
|
||||
}
|
||||
validateEntityName(notationName);
|
||||
|
||||
// Skip whitespace after notation name
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Check identifier type (SYSTEM or PUBLIC)
|
||||
const identifierType = xmlData.substring(i, i + 6).toUpperCase();
|
||||
if (identifierType !== "SYSTEM" && identifierType !== "PUBLIC") {
|
||||
throw new Error(`Expected SYSTEM or PUBLIC, found "${identifierType}"`);
|
||||
}
|
||||
i += identifierType.length;
|
||||
|
||||
// Skip whitespace after identifier type
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read public identifier (if PUBLIC)
|
||||
let publicIdentifier = null;
|
||||
let systemIdentifier = null;
|
||||
|
||||
if (identifierType === "PUBLIC") {
|
||||
[i, publicIdentifier ] = readIdentifierVal(xmlData, i, "publicIdentifier");
|
||||
|
||||
// Skip whitespace after public identifier
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Optionally read system identifier
|
||||
if (xmlData[i] === '"' || xmlData[i] === "'") {
|
||||
[i, systemIdentifier ] = readIdentifierVal(xmlData, i,"systemIdentifier");
|
||||
}
|
||||
} else if (identifierType === "SYSTEM") {
|
||||
// Read system identifier (mandatory for SYSTEM)
|
||||
[i, systemIdentifier ] = readIdentifierVal(xmlData, i, "systemIdentifier");
|
||||
|
||||
if (!systemIdentifier) {
|
||||
throw new Error("Missing mandatory system identifier for SYSTEM notation");
|
||||
}
|
||||
}
|
||||
|
||||
return {notationName, publicIdentifier, systemIdentifier, index: --i};
|
||||
}
|
||||
|
||||
function isAttlist(xmlData, i){
|
||||
if(xmlData[i+1] === '!' &&
|
||||
xmlData[i+2] === 'A' &&
|
||||
xmlData[i+3] === 'T' &&
|
||||
xmlData[i+4] === 'T' &&
|
||||
xmlData[i+5] === 'L' &&
|
||||
xmlData[i+6] === 'I' &&
|
||||
xmlData[i+7] === 'S' &&
|
||||
xmlData[i+8] === 'T') return true
|
||||
return false
|
||||
function readIdentifierVal(xmlData, i, type) {
|
||||
let identifierVal = "";
|
||||
const startChar = xmlData[i];
|
||||
if (startChar !== '"' && startChar !== "'") {
|
||||
throw new Error(`Expected quoted string, found "${startChar}"`);
|
||||
}
|
||||
i++;
|
||||
|
||||
while (i < xmlData.length && xmlData[i] !== startChar) {
|
||||
identifierVal += xmlData[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
if (xmlData[i] !== startChar) {
|
||||
throw new Error(`Unterminated ${type} value`);
|
||||
}
|
||||
i++;
|
||||
return [i, identifierVal];
|
||||
}
|
||||
function isNotation(xmlData, i){
|
||||
if(xmlData[i+1] === '!' &&
|
||||
xmlData[i+2] === 'N' &&
|
||||
xmlData[i+3] === 'O' &&
|
||||
xmlData[i+4] === 'T' &&
|
||||
xmlData[i+5] === 'A' &&
|
||||
xmlData[i+6] === 'T' &&
|
||||
xmlData[i+7] === 'I' &&
|
||||
xmlData[i+8] === 'O' &&
|
||||
xmlData[i+9] === 'N') return true
|
||||
return false
|
||||
|
||||
function readElementExp(xmlData, i) {
|
||||
// <!ELEMENT br EMPTY>
|
||||
// <!ELEMENT div ANY>
|
||||
// <!ELEMENT title (#PCDATA)>
|
||||
// <!ELEMENT book (title, author+)>
|
||||
// <!ELEMENT name (content-model)>
|
||||
|
||||
// Skip leading whitespace after <!ELEMENT
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read element name
|
||||
let elementName = "";
|
||||
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
||||
elementName += xmlData[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
// Validate element name
|
||||
if (!validateEntityName(elementName)) {
|
||||
throw new Error(`Invalid element name: "${elementName}"`);
|
||||
}
|
||||
|
||||
// Skip whitespace after element name
|
||||
i = skipWhitespace(xmlData, i);
|
||||
let contentModel = "";
|
||||
// Expect '(' to start content model
|
||||
if(xmlData[i] === "E" && hasSeq(xmlData, "MPTY",i)) i+=4;
|
||||
else if(xmlData[i] === "A" && hasSeq(xmlData, "NY",i)) i+=2;
|
||||
else if (xmlData[i] === "(") {
|
||||
i++; // Move past '('
|
||||
|
||||
// Read content model
|
||||
while (i < xmlData.length && xmlData[i] !== ")") {
|
||||
contentModel += xmlData[i];
|
||||
i++;
|
||||
}
|
||||
if (xmlData[i] !== ")") {
|
||||
throw new Error("Unterminated content model");
|
||||
}
|
||||
|
||||
}else{
|
||||
throw new Error(`Invalid Element Expression, found "${xmlData[i]}"`);
|
||||
}
|
||||
|
||||
return {
|
||||
elementName,
|
||||
contentModel: contentModel.trim(),
|
||||
index: i
|
||||
};
|
||||
}
|
||||
|
||||
function readAttlistExp(xmlData, i) {
|
||||
// Skip leading whitespace after <!ATTLIST
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read element name
|
||||
let elementName = "";
|
||||
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
||||
elementName += xmlData[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
// Validate element name
|
||||
validateEntityName(elementName)
|
||||
|
||||
// Skip whitespace after element name
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read attribute name
|
||||
let attributeName = "";
|
||||
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
||||
attributeName += xmlData[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
// Validate attribute name
|
||||
if (!validateEntityName(attributeName)) {
|
||||
throw new Error(`Invalid attribute name: "${attributeName}"`);
|
||||
}
|
||||
|
||||
// Skip whitespace after attribute name
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read attribute type
|
||||
let attributeType = "";
|
||||
if (xmlData.substring(i, i + 8).toUpperCase() === "NOTATION") {
|
||||
attributeType = "NOTATION";
|
||||
i += 8; // Move past "NOTATION"
|
||||
|
||||
// Skip whitespace after "NOTATION"
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Expect '(' to start the list of notations
|
||||
if (xmlData[i] !== "(") {
|
||||
throw new Error(`Expected '(', found "${xmlData[i]}"`);
|
||||
}
|
||||
i++; // Move past '('
|
||||
|
||||
// Read the list of allowed notations
|
||||
let allowedNotations = [];
|
||||
while (i < xmlData.length && xmlData[i] !== ")") {
|
||||
let notation = "";
|
||||
while (i < xmlData.length && xmlData[i] !== "|" && xmlData[i] !== ")") {
|
||||
notation += xmlData[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
// Validate notation name
|
||||
notation = notation.trim();
|
||||
if (!validateEntityName(notation)) {
|
||||
throw new Error(`Invalid notation name: "${notation}"`);
|
||||
}
|
||||
|
||||
allowedNotations.push(notation);
|
||||
|
||||
// Skip '|' separator or exit loop
|
||||
if (xmlData[i] === "|") {
|
||||
i++; // Move past '|'
|
||||
i = skipWhitespace(xmlData, i); // Skip optional whitespace after '|'
|
||||
}
|
||||
}
|
||||
|
||||
if (xmlData[i] !== ")") {
|
||||
throw new Error("Unterminated list of notations");
|
||||
}
|
||||
i++; // Move past ')'
|
||||
|
||||
// Store the allowed notations as part of the attribute type
|
||||
attributeType += " (" + allowedNotations.join("|") + ")";
|
||||
} else {
|
||||
// Handle simple types (e.g., CDATA, ID, IDREF, etc.)
|
||||
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
||||
attributeType += xmlData[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
// Validate simple attribute type
|
||||
const validTypes = ["CDATA", "ID", "IDREF", "IDREFS", "ENTITY", "ENTITIES", "NMTOKEN", "NMTOKENS"];
|
||||
if (!validTypes.includes(attributeType.toUpperCase())) {
|
||||
throw new Error(`Invalid attribute type: "${attributeType}"`);
|
||||
}
|
||||
}
|
||||
|
||||
// Skip whitespace after attribute type
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read default value
|
||||
let defaultValue = "";
|
||||
if (xmlData.substring(i, i + 8).toUpperCase() === "#REQUIRED") {
|
||||
defaultValue = "#REQUIRED";
|
||||
i += 8;
|
||||
} else if (xmlData.substring(i, i + 7).toUpperCase() === "#IMPLIED") {
|
||||
defaultValue = "#IMPLIED";
|
||||
i += 7;
|
||||
} else {
|
||||
[i, defaultValue] = readIdentifierVal(xmlData, i, "ATTLIST");
|
||||
}
|
||||
|
||||
return {
|
||||
elementName,
|
||||
attributeName,
|
||||
attributeType,
|
||||
defaultValue,
|
||||
index: i
|
||||
}
|
||||
}
|
||||
|
||||
function hasSeq(data, seq,i){
|
||||
for(let j=0;j<seq.length;j++){
|
||||
if(seq[j]!==data[i+j+1]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function validateEntityName(name){
|
||||
if (util.isName(name))
|
||||
if (isName(name))
|
||||
return name;
|
||||
else
|
||||
throw new Error(`Invalid entity name ${name}`);
|
||||
}
|
||||
|
||||
module.exports = readDocType;
|
||||
|
||||
+3
-5
@@ -1,5 +1,5 @@
|
||||
|
||||
const defaultOptions = {
|
||||
export const defaultOptions = {
|
||||
preserveOrder: false,
|
||||
attributeNamePrefix: '@_',
|
||||
attributesGroupName: false,
|
||||
@@ -38,11 +38,9 @@ const defaultOptions = {
|
||||
return tagName
|
||||
},
|
||||
// skipEmptyListItem: false
|
||||
captureMetaData: false,
|
||||
};
|
||||
|
||||
const buildOptions = function(options) {
|
||||
export const buildOptions = function(options) {
|
||||
return Object.assign({}, defaultOptions, options);
|
||||
};
|
||||
|
||||
exports.buildOptions = buildOptions;
|
||||
exports.defaultOptions = defaultOptions;
|
||||
Generated
Vendored
+28
-23
@@ -1,10 +1,11 @@
|
||||
'use strict';
|
||||
///@ts-check
|
||||
|
||||
const util = require('../util');
|
||||
const xmlNode = require('./xmlNode');
|
||||
const readDocType = require("./DocTypeReader");
|
||||
const toNumber = require("strnum");
|
||||
import {getAllMatches, isExist} from '../util.js';
|
||||
import xmlNode from './xmlNode.js';
|
||||
import readDocType from './DocTypeReader.js';
|
||||
import toNumber from "strnum";
|
||||
import getIgnoreAttributesFn from "../ignoreAttributes.js";
|
||||
|
||||
// const regx =
|
||||
// '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)'
|
||||
@@ -13,7 +14,7 @@ const toNumber = require("strnum");
|
||||
//const tagsRegx = new RegExp("<(\\/?[\\w:\\-\._]+)([^>]*)>(\\s*"+cdataRegx+")*([^<]+)?","g");
|
||||
//const tagsRegx = new RegExp("<(\\/?)((\\w*:)?([\\w:\\-\._]+))([^>]*)>([^<]*)("+cdataRegx+"([^<]*))*([^<]+)?","g");
|
||||
|
||||
class OrderedObjParser{
|
||||
export default class OrderedObjParser{
|
||||
constructor(options){
|
||||
this.options = options;
|
||||
this.currentNode = null;
|
||||
@@ -40,8 +41,8 @@ class OrderedObjParser{
|
||||
"copyright" : { regex: /&(copy|#169);/g, val: "©" },
|
||||
"reg" : { regex: /&(reg|#174);/g, val: "®" },
|
||||
"inr" : { regex: /&(inr|#8377);/g, val: "₹" },
|
||||
"num_dec": { regex: /&#([0-9]{1,7});/g, val : (_, str) => String.fromCharCode(Number.parseInt(str, 10)) },
|
||||
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val : (_, str) => String.fromCharCode(Number.parseInt(str, 16)) },
|
||||
"num_dec": { regex: /&#([0-9]{1,7});/g, val : (_, str) => String.fromCodePoint(Number.parseInt(str, 10)) },
|
||||
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val : (_, str) => String.fromCodePoint(Number.parseInt(str, 16)) },
|
||||
};
|
||||
this.addExternalEntities = addExternalEntities;
|
||||
this.parseXml = parseXml;
|
||||
@@ -53,6 +54,7 @@ class OrderedObjParser{
|
||||
this.readStopNodeData = readStopNodeData;
|
||||
this.saveTextToParentTag = saveTextToParentTag;
|
||||
this.addChild = addChild;
|
||||
this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -125,15 +127,18 @@ function resolveNameSpace(tagname) {
|
||||
const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm');
|
||||
|
||||
function buildAttributesMap(attrStr, jPath, tagName) {
|
||||
if (!this.options.ignoreAttributes && typeof attrStr === 'string') {
|
||||
if (this.options.ignoreAttributes !== true && typeof attrStr === 'string') {
|
||||
// attrStr = attrStr.replace(/\r?\n/g, ' ');
|
||||
//attrStr = attrStr || attrStr.trim();
|
||||
|
||||
const matches = util.getAllMatches(attrStr, attrsRegx);
|
||||
const matches = getAllMatches(attrStr, attrsRegx);
|
||||
const len = matches.length; //don't make it inline
|
||||
const attrs = {};
|
||||
for (let i = 0; i < len; i++) {
|
||||
const attrName = this.resolveNameSpace(matches[i][1]);
|
||||
if (this.ignoreAttributesFn(attrName, jPath)) {
|
||||
continue
|
||||
}
|
||||
let oldVal = matches[i][4];
|
||||
let aName = this.options.attributeNamePrefix + attrName;
|
||||
if (attrName.length) {
|
||||
@@ -241,8 +246,7 @@ const parseXml = function(xmlData) {
|
||||
if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){
|
||||
childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName);
|
||||
}
|
||||
this.addChild(currentNode, childNode, jPath)
|
||||
|
||||
this.addChild(currentNode, childNode, jPath, i);
|
||||
}
|
||||
|
||||
|
||||
@@ -307,6 +311,7 @@ const parseXml = function(xmlData) {
|
||||
if(tagName !== xmlObj.tagname){
|
||||
jPath += jPath ? "." + tagName : tagName;
|
||||
}
|
||||
const startIndex = i;
|
||||
if (this.isItStopNode(this.options.stopNodes, jPath, tagName)) {
|
||||
let tagContent = "";
|
||||
//self-closing tag
|
||||
@@ -335,6 +340,7 @@ const parseXml = function(xmlData) {
|
||||
}
|
||||
|
||||
const childNode = new xmlNode(tagName);
|
||||
|
||||
if(tagName !== tagExp && attrExpPresent){
|
||||
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
|
||||
}
|
||||
@@ -345,7 +351,7 @@ const parseXml = function(xmlData) {
|
||||
jPath = jPath.substr(0, jPath.lastIndexOf("."));
|
||||
childNode.add(this.options.textNodeName, tagContent);
|
||||
|
||||
this.addChild(currentNode, childNode, jPath)
|
||||
this.addChild(currentNode, childNode, jPath, startIndex);
|
||||
}else{
|
||||
//selfClosing tag
|
||||
if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
|
||||
@@ -365,7 +371,7 @@ const parseXml = function(xmlData) {
|
||||
if(tagName !== tagExp && attrExpPresent){
|
||||
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
|
||||
}
|
||||
this.addChild(currentNode, childNode, jPath)
|
||||
this.addChild(currentNode, childNode, jPath, startIndex);
|
||||
jPath = jPath.substr(0, jPath.lastIndexOf("."));
|
||||
}
|
||||
//opening tag
|
||||
@@ -376,7 +382,7 @@ const parseXml = function(xmlData) {
|
||||
if(tagName !== tagExp && attrExpPresent){
|
||||
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
|
||||
}
|
||||
this.addChild(currentNode, childNode, jPath)
|
||||
this.addChild(currentNode, childNode, jPath, startIndex);
|
||||
currentNode = childNode;
|
||||
}
|
||||
textData = "";
|
||||
@@ -390,14 +396,16 @@ const parseXml = function(xmlData) {
|
||||
return xmlObj.child;
|
||||
}
|
||||
|
||||
function addChild(currentNode, childNode, jPath){
|
||||
function addChild(currentNode, childNode, jPath, startIndex){
|
||||
// unset startIndex if not requested
|
||||
if (!this.options.captureMetaData) startIndex = undefined;
|
||||
const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"])
|
||||
if(result === false){
|
||||
}else if(typeof result === "string"){
|
||||
} else if(typeof result === "string"){
|
||||
childNode.tagname = result
|
||||
currentNode.addChild(childNode);
|
||||
currentNode.addChild(childNode, startIndex);
|
||||
}else{
|
||||
currentNode.addChild(childNode);
|
||||
currentNode.addChild(childNode, startIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,7 +432,7 @@ const replaceEntitiesValue = function(val){
|
||||
}
|
||||
function saveTextToParentTag(textData, currentNode, jPath, isLeafNode) {
|
||||
if (textData) { //store previously collected data as textNode
|
||||
if(isLeafNode === undefined) isLeafNode = Object.keys(currentNode.child).length === 0
|
||||
if(isLeafNode === undefined) isLeafNode = currentNode.child.length === 0
|
||||
|
||||
textData = this.parseTextData(textData,
|
||||
currentNode.tagname,
|
||||
@@ -589,13 +597,10 @@ function parseValue(val, shouldParse, options) {
|
||||
else if(newval === 'false' ) return false;
|
||||
else return toNumber(val, options);
|
||||
} else {
|
||||
if (util.isExist(val)) {
|
||||
if (isExist(val)) {
|
||||
return val;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = OrderedObjParser;
|
||||
|
||||
+21
-8
@@ -1,9 +1,10 @@
|
||||
const { buildOptions} = require("./OptionsBuilder");
|
||||
const OrderedObjParser = require("./OrderedObjParser");
|
||||
const { prettify} = require("./node2json");
|
||||
const validator = require('../validator');
|
||||
import { buildOptions} from './OptionsBuilder.js';
|
||||
import OrderedObjParser from './OrderedObjParser.js';
|
||||
import prettify from './node2json.js';
|
||||
import {validate} from "../validator.js";
|
||||
import XmlNode from './xmlNode.js';
|
||||
|
||||
class XMLParser{
|
||||
export default class XMLParser{
|
||||
|
||||
constructor(options){
|
||||
this.externalEntities = {};
|
||||
@@ -25,7 +26,7 @@ class XMLParser{
|
||||
if( validationOption){
|
||||
if(validationOption === true) validationOption = {}; //validate with default options
|
||||
|
||||
const result = validator.validate(xmlData, validationOption);
|
||||
const result = validate(xmlData, validationOption);
|
||||
if (result !== true) {
|
||||
throw Error( `${result.err.msg}:${result.err.line}:${result.err.col}` )
|
||||
}
|
||||
@@ -53,6 +54,18 @@ class XMLParser{
|
||||
this.externalEntities[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = XMLParser;
|
||||
/**
|
||||
* Returns a Symbol that can be used to access the metadata
|
||||
* property on a node.
|
||||
*
|
||||
* If Symbol is not available in the environment, an ordinary property is used
|
||||
* and the name of the property is here returned.
|
||||
*
|
||||
* The XMLMetaData property is only present when `captureMetaData`
|
||||
* is true in the options.
|
||||
*/
|
||||
static getMetaDataSymbol() {
|
||||
return XmlNode.getMetaDataSymbol();
|
||||
}
|
||||
}
|
||||
|
||||
+8
-2
@@ -1,12 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
import XmlNode from './xmlNode.js';
|
||||
|
||||
const METADATA_SYMBOL = XmlNode.getMetaDataSymbol();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {array} node
|
||||
* @param {any} options
|
||||
* @returns
|
||||
*/
|
||||
function prettify(node, options){
|
||||
export default function prettify(node, options){
|
||||
return compress( node, options);
|
||||
}
|
||||
|
||||
@@ -36,6 +40,9 @@ function compress(arr, options, jPath){
|
||||
|
||||
let val = compress(tagObj[property], options, newJpath);
|
||||
const isLeaf = isLeafTag(val, options);
|
||||
if (tagObj[METADATA_SYMBOL] !== undefined) {
|
||||
val[METADATA_SYMBOL] = tagObj[METADATA_SYMBOL]; // copy over metadata
|
||||
}
|
||||
|
||||
if(tagObj[":@"]){
|
||||
assignAttributes( val, tagObj[":@"], newJpath, options);
|
||||
@@ -110,4 +117,3 @@ function isLeafTag(obj, options){
|
||||
|
||||
return false;
|
||||
}
|
||||
exports.prettify = prettify;
|
||||
|
||||
+22
-7
@@ -1,6 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
class XmlNode{
|
||||
let METADATA_SYMBOL;
|
||||
|
||||
if (typeof Symbol !== "function") {
|
||||
METADATA_SYMBOL = "@@xmlMetadata";
|
||||
} else {
|
||||
METADATA_SYMBOL = Symbol("XML Node Metadata");
|
||||
}
|
||||
|
||||
export default class XmlNode{
|
||||
constructor(tagname) {
|
||||
this.tagname = tagname;
|
||||
this.child = []; //nested tags, text, cdata, comments in order
|
||||
@@ -11,15 +19,22 @@ class XmlNode{
|
||||
if(key === "__proto__") key = "#__proto__";
|
||||
this.child.push( {[key]: val });
|
||||
}
|
||||
addChild(node) {
|
||||
addChild(node, startIndex) {
|
||||
if(node.tagname === "__proto__") node.tagname = "#__proto__";
|
||||
if(node[":@"] && Object.keys(node[":@"]).length > 0){
|
||||
this.child.push( { [node.tagname]: node.child, [":@"]: node[":@"] });
|
||||
}else{
|
||||
this.child.push( { [node.tagname]: node.child });
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
module.exports = XmlNode;
|
||||
// if requested, add the startIndex
|
||||
if (startIndex !== undefined) {
|
||||
// Note: for now we just overwrite the metadata. If we had more complex metadata,
|
||||
// we might need to do an object append here: metadata = { ...metadata, startIndex }
|
||||
this.child[this.child.length - 1][METADATA_SYMBOL] = { startIndex };
|
||||
}
|
||||
}
|
||||
/** symbol used for metadata */
|
||||
static getMetaDataSymbol() {
|
||||
return METADATA_SYMBOL;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user