Initial commit
This commit is contained in:
commit
e49726988c
5445 files changed
+1089914
No files matched your search
+63
@@ -0,0 +1,63 @@
|
||||
# A simple XML to JSON converter
|
||||
|
||||
[](https://www.npmjs.com/package/simple-xml-to-json)
|
||||
[](https://github.com/nirgit/simple-xml-to-json/actions/workflows/node.js.yml)
|
||||
[](https://codecov.io/gh/nirgit/simple-xml-to-json)
|
||||
|
||||
## Install
|
||||
Simply install using NPM in your project directory
|
||||
> npm install simple-xml-to-json
|
||||
|
||||
|
||||
## Usage and API
|
||||
### 1. convertXML(xmlToConvert [,customConverter])
|
||||
* `xmlToConvert` \<string\>
|
||||
* `customConverter` \<function\>
|
||||
* Returns: \<JSON\> by default or other if `customConverter` is used
|
||||
|
||||
### 2. createAST(xmlToConvert)
|
||||
* `xmlToConvert` \<string\>
|
||||
* Returns: An AST representation of the XML \<JSON\>
|
||||
|
||||
Code Example:
|
||||
```javascript
|
||||
const {convertXML, createAST} = require("simple-xml-to-json")
|
||||
|
||||
const myJson = convertXML(myXMLString)
|
||||
const myYaml = convertXML(myXMLString, yamlConverter)
|
||||
const myAst = createAST(myXMLString)
|
||||
````
|
||||
|
||||
---
|
||||
###  Typescript compatible
|
||||
---
|
||||
|
||||
## Notes and how to use code
|
||||
1. The easiest thing to start is to run `node example/example.js` in your terminal and see what happens.
|
||||
2. There's the `xmlToJson.js` file for convenience. Just pass in the XML as a String.
|
||||
3. It's MIT licensed so you can do whatever :)
|
||||
4. Profit
|
||||
|
||||
## How this works in a nutshell
|
||||
1. The library converts the XML to an AST
|
||||
2. There is a JSON converter that takes the AST and spits out a JSON
|
||||
3. You can write your own converters if you need XML-to-ANY-OTHER-FORMAT
|
||||
|
||||
## Benchmark
|
||||
|
||||
_Take these results with a grain of salt._\
|
||||
According to a __simple__ benchmark test I performed in __April 2024__ with a random XML. _YMMV_.
|
||||

|
||||
|
||||
* The chart results __may be__ outdated. [Run the test yourself](https://runkit.com/nirgit/6620cf27943a41000847221d)
|
||||
|
||||
|
||||
## Current Drawbacks
|
||||
1. All values are translated to strings in JSON
|
||||
2. There are currently reserved words in the JSON converter:
|
||||
* "content"
|
||||
* "children"
|
||||
|
||||
so you cannot by default have an attribute with that name and free text as the content of the element or have nested elements as children.
|
||||
|
||||
*If you need to, you can write your own converter from the AST created by the parser, and pass it as a 2nd parameter after the xml string*
|
||||
+505
@@ -0,0 +1,505 @@
|
||||
'use strict';
|
||||
|
||||
function getDefaultExportFromCjs (x) {
|
||||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
||||
}
|
||||
|
||||
const convertToJSON = ast => {
|
||||
return buildJSONFromNode(ast.value.children[0])
|
||||
};
|
||||
|
||||
const buildJSONFromNode = node => {
|
||||
if (!node) return null
|
||||
const json = {};
|
||||
switch (node.type) {
|
||||
case "ELEMENT": {
|
||||
let element = {};
|
||||
const attribs = buildAttributes(node.value.attributes);
|
||||
const children = buildJSONFromNode(node.value.children);
|
||||
if (attribs) {
|
||||
element = Object.assign(element, attribs);
|
||||
}
|
||||
if (children) {
|
||||
const jsonChildren = buildChildren(node.value.children);
|
||||
element = Object.assign(element, jsonChildren);
|
||||
}
|
||||
json[node.value.type] = element;
|
||||
break
|
||||
}
|
||||
case "ATTRIBUTE": {
|
||||
const attribNameAndValue = node.value;
|
||||
json[attribNameAndValue.name] = attribNameAndValue.value;
|
||||
break
|
||||
}
|
||||
case "CONTENT": {
|
||||
return {content: node.value}
|
||||
}
|
||||
}
|
||||
|
||||
return json
|
||||
};
|
||||
|
||||
const buildChildren = children => {
|
||||
if (!children || !Array.isArray(children) || children.length === 0) return null
|
||||
if (isContentChildren(children)) {
|
||||
return {
|
||||
content: children[0].value
|
||||
}
|
||||
}
|
||||
return {
|
||||
children: children.map(buildJSONFromNode)
|
||||
}
|
||||
};
|
||||
|
||||
const isContentChildren = children => children && Array.isArray(children) && children.length === 1 && children[0].type === "CONTENT";
|
||||
|
||||
const buildAttributes = arrayNodes => {
|
||||
if (arrayNodes && Array.isArray(arrayNodes)) {
|
||||
const jsonArray = arrayNodes.map(buildJSONFromNode);
|
||||
return jsonArray.reduce((agg, j) => Object.assign(agg, j), {})
|
||||
}
|
||||
return null
|
||||
};
|
||||
|
||||
var astToJson = {
|
||||
convert: convertToJSON
|
||||
};
|
||||
|
||||
const Token$2 = (type, value) => ({
|
||||
type,
|
||||
value
|
||||
});
|
||||
|
||||
var model = {
|
||||
Token: Token$2
|
||||
};
|
||||
|
||||
const { Token: Token$1 } = model;
|
||||
|
||||
const EOF_TOKEN = Token$1("EOF");
|
||||
|
||||
const isCharBlank = (char) =>
|
||||
char === ' ' || char === '\n' || char === '\r' || char === '\t';
|
||||
|
||||
const skipXMLDocumentHeader = (xmlAsString, pos) => {
|
||||
if (xmlAsString.startsWith('<?xml', pos)) {
|
||||
const len = xmlAsString.length;
|
||||
while (pos < len) {
|
||||
if (xmlAsString[pos] !== '?') {
|
||||
pos++;
|
||||
} else if (xmlAsString[pos + 1] === '>') {
|
||||
return pos + 2 // skip "?>""
|
||||
} else {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pos
|
||||
};
|
||||
|
||||
const replaceQuotes = (str) => str.replace(/'/g, "'");
|
||||
|
||||
const getInitialPosForLexer = (xmlAsString) => {
|
||||
let pos = 0;
|
||||
while (pos < xmlAsString.length && isCharBlank(xmlAsString[pos])) pos++;
|
||||
return skipXMLDocumentHeader(xmlAsString, pos)
|
||||
};
|
||||
|
||||
function createLexer$1(xmlAsString) {
|
||||
let currentToken = null;
|
||||
let pos = getInitialPosForLexer(xmlAsString);
|
||||
let scopingElement = [];
|
||||
|
||||
const peek = () => xmlAsString[pos];
|
||||
const hasNext = () => currentToken !== EOF_TOKEN && pos < xmlAsString.length;
|
||||
const isBlankSpace = () => isCharBlank(xmlAsString[pos]);
|
||||
|
||||
const skipQuotes = () => {
|
||||
if (hasNext() && isQuote(peek())) pos++;
|
||||
};
|
||||
|
||||
const isQuote = (char) => '"' === char || "'" === char;
|
||||
|
||||
const skipSpaces = () => {
|
||||
while (hasNext() && isBlankSpace()) pos++;
|
||||
};
|
||||
|
||||
const readAlphaNumericCharsOrBrackets = (areSpecialCharsSupported) => {
|
||||
if (hasNext()) {
|
||||
if (xmlAsString[pos] === '<') {
|
||||
let buffer = '<';
|
||||
pos++;
|
||||
if (hasNext() && xmlAsString[pos] === '/') {
|
||||
pos++;
|
||||
buffer = '</';
|
||||
} else if (
|
||||
hasNext() &&
|
||||
xmlAsString[pos] === '!' &&
|
||||
xmlAsString[pos + 1] === '-' &&
|
||||
xmlAsString[pos + 2] === '-'
|
||||
) {
|
||||
// its a comment
|
||||
pos++;
|
||||
pos++;
|
||||
pos++;
|
||||
buffer = '<!--';
|
||||
}
|
||||
return buffer
|
||||
} else if (peek() === '/') {
|
||||
let buffer = '/';
|
||||
pos++;
|
||||
if (hasNext() && peek() === '>') {
|
||||
pos++;
|
||||
buffer = '/>';
|
||||
}
|
||||
return buffer
|
||||
} else if (xmlAsString[pos] === '=' || xmlAsString[pos] === '>') {
|
||||
const buffer = xmlAsString[pos];
|
||||
pos++;
|
||||
return buffer
|
||||
}
|
||||
}
|
||||
return readAlphaNumericChars(!!areSpecialCharsSupported)
|
||||
};
|
||||
|
||||
const readAlphaNumericChars = (areSpecialCharsSupported) => {
|
||||
const ELEMENT_TYPE_MATCHER = /[a-zA-Z0-9_:-]/;
|
||||
const NAMES_VALS_CONTENT_MATCHER = /[^>=<]/u;
|
||||
const matcher = areSpecialCharsSupported
|
||||
? NAMES_VALS_CONTENT_MATCHER
|
||||
: ELEMENT_TYPE_MATCHER;
|
||||
let start = pos;
|
||||
while (hasNext() && xmlAsString[pos].match(matcher)) pos++;
|
||||
return replaceQuotes(xmlAsString.substring(start, pos))
|
||||
};
|
||||
|
||||
const isElementBegin = () =>
|
||||
currentToken && currentToken.type === "OPEN_BRACKET";
|
||||
const isAssignToAttribute = () =>
|
||||
currentToken && currentToken.type === "ASSIGN";
|
||||
|
||||
const next = () => {
|
||||
const prevPos = pos;
|
||||
skipSpaces();
|
||||
const numOfSpacesSkipped = pos - prevPos;
|
||||
if (!hasNext()) {
|
||||
currentToken = EOF_TOKEN;
|
||||
} else if (isElementBegin()) {
|
||||
// starting new element
|
||||
skipSpaces();
|
||||
const buffer = readAlphaNumericCharsOrBrackets(false);
|
||||
currentToken = Token$1("ELEMENT_TYPE", buffer);
|
||||
scopingElement.push(buffer);
|
||||
} else if (isAssignToAttribute()) {
|
||||
// assign value to attribute
|
||||
skipQuotes();
|
||||
const openingQuote = xmlAsString[pos - 1];
|
||||
let start = pos;
|
||||
while (hasNext() && peek() !== openingQuote) pos++;
|
||||
const buffer = replaceQuotes(xmlAsString.substring(start, pos));
|
||||
pos++;
|
||||
currentToken = Token$1("ATTRIB_VALUE", buffer);
|
||||
} else {
|
||||
skipSpaces();
|
||||
let buffer = readAlphaNumericCharsOrBrackets(true);
|
||||
switch (buffer) {
|
||||
case '=': {
|
||||
if (currentToken.type === "ATTRIB_NAME") {
|
||||
currentToken = Token$1("ASSIGN");
|
||||
} else {
|
||||
currentToken = Token$1("CONTENT", buffer);
|
||||
}
|
||||
break
|
||||
}
|
||||
case '</': {
|
||||
const start = pos;
|
||||
while (xmlAsString[pos] !== '>') pos++;
|
||||
currentToken = Token$1(
|
||||
"CLOSE_ELEMENT",
|
||||
xmlAsString.substring(start, pos)
|
||||
);
|
||||
pos++; // skip the ">"
|
||||
scopingElement.pop();
|
||||
break
|
||||
}
|
||||
case '/>': {
|
||||
const scopingElementName = scopingElement.pop();
|
||||
currentToken = Token$1(
|
||||
"CLOSE_ELEMENT",
|
||||
scopingElementName
|
||||
);
|
||||
break
|
||||
}
|
||||
case '<!--': {
|
||||
// skipComment contents
|
||||
const closingBuff = ['!', '-', '-'];
|
||||
while (
|
||||
hasNext() &&
|
||||
(closingBuff[2] !== '>' ||
|
||||
closingBuff[1] !== '-' ||
|
||||
closingBuff[0] !== '-')
|
||||
) {
|
||||
closingBuff.shift();
|
||||
closingBuff.push(xmlAsString[pos]);
|
||||
pos++;
|
||||
}
|
||||
return next()
|
||||
}
|
||||
case '>': {
|
||||
currentToken = Token$1("CLOSE_BRACKET");
|
||||
break
|
||||
}
|
||||
case '<': {
|
||||
currentToken = Token$1("OPEN_BRACKET");
|
||||
break
|
||||
}
|
||||
default: {
|
||||
// here we fall if we have alphanumeric string, which can be related to attributes, content or nothing
|
||||
if (buffer && buffer.length > 0) {
|
||||
if (currentToken.type === "CLOSE_BRACKET") {
|
||||
let suffix = '';
|
||||
if (peek() !== '<') {
|
||||
suffix = readAlphaNumericChars(true);
|
||||
}
|
||||
currentToken = Token$1(
|
||||
"CONTENT",
|
||||
buffer + suffix
|
||||
);
|
||||
} else if (
|
||||
currentToken.type !== "ATTRIB_NAME" &&
|
||||
currentToken.type !== "CONTENT"
|
||||
) {
|
||||
if (
|
||||
currentToken.type === "CLOSE_ELEMENT"
|
||||
) {
|
||||
// we're assuming this is content, part of unstructured data
|
||||
buffer = ' '.repeat(numOfSpacesSkipped) + buffer;
|
||||
currentToken = Token$1("CONTENT", buffer);
|
||||
} else {
|
||||
// it should be an attribute name token
|
||||
currentToken = Token$1(
|
||||
"ATTRIB_NAME",
|
||||
buffer
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const contentBuffer =
|
||||
' '.repeat(numOfSpacesSkipped) + buffer; // spaces included as content
|
||||
currentToken = Token$1(
|
||||
"CONTENT",
|
||||
contentBuffer
|
||||
);
|
||||
}
|
||||
break
|
||||
} else {
|
||||
const errMsg =
|
||||
'Unknown Syntax : "' + xmlAsString[pos] + '"';
|
||||
throw new Error(errMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return currentToken
|
||||
};
|
||||
|
||||
return {
|
||||
peek,
|
||||
next,
|
||||
hasNext,
|
||||
// prettier-ignore
|
||||
...({})
|
||||
}
|
||||
}
|
||||
|
||||
var lexer = {
|
||||
createLexer: createLexer$1
|
||||
};
|
||||
|
||||
const { createLexer } = lexer;
|
||||
const { Token } = model;
|
||||
// AST Node types
|
||||
const [ROOT, ELEMENT, ATTRIBUTE, CONTENT] = [
|
||||
'ROOT',
|
||||
'ELEMENT',
|
||||
'ATTRIBUTE',
|
||||
'CONTENT'
|
||||
];
|
||||
|
||||
const Node = (type, value) => ({
|
||||
type,
|
||||
value
|
||||
});
|
||||
|
||||
const ContentNode = (value) => Node(CONTENT, value);
|
||||
|
||||
const ElementNode = (type, attributes, children) => {
|
||||
return Node(ELEMENT, {
|
||||
type,
|
||||
attributes,
|
||||
children
|
||||
})
|
||||
};
|
||||
|
||||
const AttribNode = (name, value) => {
|
||||
return Node(ATTRIBUTE, {
|
||||
name,
|
||||
value
|
||||
})
|
||||
};
|
||||
|
||||
const parseXML = (lexer) => {
|
||||
/*
|
||||
How does the grammar look?
|
||||
| expr: StructuredXML | UnstructuredXML | Content
|
||||
| StructuredXML: (openBracket + ElementName) + (AttributeList)* + closeBracket + (expr)* + closeElement
|
||||
| UnstructuredXML: Content* + expr* + Content*
|
||||
| Content: String
|
||||
| openBracket: <
|
||||
| closeBracket: >
|
||||
| closeElement: </ + ElementName + closeBracket
|
||||
| ElementName: String
|
||||
| AttributeList: AttributeName + "=" + AttributeValue + AttributeList*
|
||||
| AttributeName: String
|
||||
| AttributeValue: String
|
||||
*/
|
||||
const rootNode = Node(ROOT, {
|
||||
children: parseExpr(lexer, Token(ROOT, 'ROOT'))
|
||||
});
|
||||
return rootNode
|
||||
};
|
||||
|
||||
const parseExpr = (lexer, scopingElement) => {
|
||||
const children = [];
|
||||
while (lexer.hasNext()) {
|
||||
const lexem = lexer.next();
|
||||
switch (lexem.type) {
|
||||
case "OPEN_BRACKET": {
|
||||
const elementLexem = lexer.next();
|
||||
const [elementAttributes, currentToken] =
|
||||
parseElementAttributes(lexer);
|
||||
let elementChildren = [];
|
||||
if (currentToken.type !== "CLOSE_ELEMENT") {
|
||||
elementChildren = parseExpr(lexer, elementLexem);
|
||||
}
|
||||
if (
|
||||
elementChildren &&
|
||||
elementChildren.length > 0 &&
|
||||
elementChildren[0].type === "CONTENT"
|
||||
) {
|
||||
elementChildren = reduceChildrenElements(elementChildren);
|
||||
}
|
||||
children.push(
|
||||
ElementNode(
|
||||
elementLexem.value,
|
||||
elementAttributes,
|
||||
elementChildren
|
||||
)
|
||||
);
|
||||
break
|
||||
}
|
||||
case "CLOSE_ELEMENT": {
|
||||
if (lexem.value === scopingElement.value) return children
|
||||
break
|
||||
}
|
||||
case "CONTENT": {
|
||||
children.push(ContentNode(lexem.value));
|
||||
break
|
||||
}
|
||||
case "EOF": {
|
||||
return children
|
||||
}
|
||||
default: {
|
||||
throw new Error(
|
||||
`Unknown Lexem type: ${lexem.type} "${lexem.value}, scoping element: ${scopingElement.value}"`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return children
|
||||
};
|
||||
|
||||
const parseElementAttributes = (lexer) => {
|
||||
const attribs = [];
|
||||
let currentToken = lexer.peek();
|
||||
if (
|
||||
!lexer.hasNext() ||
|
||||
(currentToken && currentToken.type === "CLOSE_BRACKET") ||
|
||||
(currentToken && currentToken.type === "CLOSE_ELEMENT")
|
||||
) {
|
||||
return [attribs, currentToken]
|
||||
}
|
||||
currentToken = lexer.next();
|
||||
while (
|
||||
lexer.hasNext() &&
|
||||
currentToken &&
|
||||
currentToken.type !== "CLOSE_BRACKET" &&
|
||||
currentToken.type !== "CLOSE_ELEMENT"
|
||||
) {
|
||||
const attribName = currentToken;
|
||||
lexer.next(); //assignment token
|
||||
const attribValue = lexer.next();
|
||||
const attributeNode = AttribNode(attribName.value, attribValue.value);
|
||||
attribs.push(attributeNode);
|
||||
currentToken = lexer.next();
|
||||
}
|
||||
return [attribs, currentToken]
|
||||
};
|
||||
|
||||
function reduceChildrenElements(elementChildren) {
|
||||
let reduced = [],
|
||||
buffer = '';
|
||||
|
||||
elementChildren.forEach((child) => {
|
||||
if (child.type === "CONTENT") {
|
||||
buffer += child.value;
|
||||
} else {
|
||||
if (buffer.length) {
|
||||
reduced.push(ContentNode(buffer));
|
||||
buffer = '';
|
||||
}
|
||||
reduced.push(child);
|
||||
}
|
||||
});
|
||||
|
||||
if (buffer.length) reduced.push(ContentNode(buffer));
|
||||
|
||||
return reduced
|
||||
}
|
||||
|
||||
function transpile$1(xmlAsString, astConverter) {
|
||||
const lexer = createLexer(xmlAsString);
|
||||
const ast = parseXML(lexer);
|
||||
if (astConverter) {
|
||||
return astConverter.convert(ast)
|
||||
}
|
||||
return ast
|
||||
}
|
||||
|
||||
var transpiler = {
|
||||
AttribNode,
|
||||
ContentNode,
|
||||
ElementNode,
|
||||
Node,
|
||||
transpile: transpile$1
|
||||
};
|
||||
|
||||
const jsonConverter = astToJson;
|
||||
const { transpile } = transpiler;
|
||||
|
||||
function convertXML(xmlAsString, customConverter) {
|
||||
return transpile(xmlAsString, customConverter || jsonConverter)
|
||||
}
|
||||
|
||||
function createAST(xmlAsString) {
|
||||
return transpile(xmlAsString)
|
||||
}
|
||||
|
||||
var xmlToJson = {
|
||||
convertXML,
|
||||
createAST
|
||||
};
|
||||
|
||||
var xmlToJson$1 = /*@__PURE__*/getDefaultExportFromCjs(xmlToJson);
|
||||
|
||||
module.exports = xmlToJson$1;
|
||||
+1
@@ -0,0 +1 @@
|
||||
"use strict";function e(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}const t=e=>{if(!e)return null;const n={};switch(e.type){case"ELEMENT":{let a={};const u=s(e.value.attributes),c=t(e.value.children);if(u&&(a=Object.assign(a,u)),c){const t=r(e.value.children);a=Object.assign(a,t)}n[e.value.type]=a;break}case"ATTRIBUTE":{const t=e.value;n[t.name]=t.value;break}case"CONTENT":return{content:e.value}}return n},r=e=>e&&Array.isArray(e)&&0!==e.length?n(e)?{content:e[0].value}:{children:e.map(t)}:null,n=e=>e&&Array.isArray(e)&&1===e.length&&"CONTENT"===e[0].type,s=e=>{if(e&&Array.isArray(e)){return e.map(t).reduce(((e,t)=>Object.assign(e,t)),{})}return null};var a={convert:e=>t(e.value.children[0])};var u={Token:(e,t)=>({type:e,value:t})};const{Token:c}=u,l=c("EOF"),E=e=>" "===e||"\n"===e||"\r"===e||"\t"===e,o=e=>e.replace(/'/g,"'");var T={createLexer:function(e){let t=null,r=(e=>{let t=0;for(;t<e.length&&E(e[t]);)t++;return((e,t)=>{if(e.startsWith("<?xml",t)){const r=e.length;for(;t<r;)if("?"!==e[t])t++;else{if(">"===e[t+1])return t+2;t++}}return t})(e,t)})(e),n=[];const s=()=>e[r],a=()=>t!==l&&r<e.length,u=e=>'"'===e||"'"===e,T=()=>{for(;a()&&E(e[r]);)r++},i=t=>{if(a()){if("<"===e[r]){let t="<";return r++,a()&&"/"===e[r]?(r++,t="</"):a()&&"!"===e[r]&&"-"===e[r+1]&&"-"===e[r+2]&&(r++,r++,r++,t="\x3c!--"),t}if("/"===s()){let e="/";return r++,a()&&">"===s()&&(r++,e="/>"),e}if("="===e[r]||">"===e[r]){const t=e[r];return r++,t}}return p(!!t)},p=t=>{const n=t?/[^>=<]/u:/[a-zA-Z0-9_:-]/;let s=r;for(;a()&&e[r].match(n);)r++;return o(e.substring(s,r))},N=()=>{const E=r;T();const f=r-E;if(a())if(t&&"OPEN_BRACKET"===t.type){T();const e=i(!1);t=c("ELEMENT_TYPE",e),n.push(e)}else if(t&&"ASSIGN"===t.type){a()&&u(s())&&r++;const n=e[r-1];let l=r;for(;a()&&s()!==n;)r++;const E=o(e.substring(l,r));r++,t=c("ATTRIB_VALUE",E)}else{T();let u=i(!0);switch(u){case"=":t="ATTRIB_NAME"===t.type?c("ASSIGN"):c("CONTENT",u);break;case"</":{const s=r;for(;">"!==e[r];)r++;t=c("CLOSE_ELEMENT",e.substring(s,r)),r++,n.pop();break}case"/>":{const e=n.pop();t=c("CLOSE_ELEMENT",e);break}case"\x3c!--":{const t=["!","-","-"];for(;a()&&(">"!==t[2]||"-"!==t[1]||"-"!==t[0]);)t.shift(),t.push(e[r]),r++;return N()}case">":t=c("CLOSE_BRACKET");break;case"<":t=c("OPEN_BRACKET");break;default:if(u&&u.length>0){if("CLOSE_BRACKET"===t.type){let e="";"<"!==s()&&(e=p(!0)),t=c("CONTENT",u+e)}else if("ATTRIB_NAME"!==t.type&&"CONTENT"!==t.type)"CLOSE_ELEMENT"===t.type?(u=" ".repeat(f)+u,t=c("CONTENT",u)):t=c("ATTRIB_NAME",u);else{const e=" ".repeat(f)+u;t=c("CONTENT",e)}break}{const t='Unknown Syntax : "'+e[r]+'"';throw new Error(t)}}}else t=l;return t};return{peek:s,next:N,hasNext:a}}};const{createLexer:i}=T,{Token:p}=u,[N,f,h,O]=["ROOT","ELEMENT","ATTRIBUTE","CONTENT"],y=(e,t)=>({type:e,value:t}),C=e=>y(O,e),v=(e,t,r)=>y(f,{type:e,attributes:t,children:r}),A=(e,t)=>y(h,{name:e,value:t}),L=(e,t)=>{const r=[];for(;e.hasNext();){const n=e.next();switch(n.type){case"OPEN_BRACKET":{const t=e.next(),[n,s]=_(e);let a=[];"CLOSE_ELEMENT"!==s.type&&(a=L(e,t)),a&&a.length>0&&"CONTENT"===a[0].type&&(a=b(a)),r.push(v(t.value,n,a));break}case"CLOSE_ELEMENT":if(n.value===t.value)return r;break;case"CONTENT":r.push(C(n.value));break;case"EOF":return r;default:throw new Error(`Unknown Lexem type: ${n.type} "${n.value}, scoping element: ${t.value}"`)}}return r},_=e=>{const t=[];let r=e.peek();if(!e.hasNext()||r&&"CLOSE_BRACKET"===r.type||r&&"CLOSE_ELEMENT"===r.type)return[t,r];for(r=e.next();e.hasNext()&&r&&"CLOSE_BRACKET"!==r.type&&"CLOSE_ELEMENT"!==r.type;){const n=r;e.next();const s=e.next(),a=A(n.value,s.value);t.push(a),r=e.next()}return[t,r]};function b(e){let t=[],r="";return e.forEach((e=>{"CONTENT"===e.type?r+=e.value:(r.length&&(t.push(C(r)),r=""),t.push(e))})),r.length&&t.push(C(r)),t}var x={AttribNode:A,ContentNode:C,ElementNode:v,Node:y,transpile:function(e,t){const r=(e=>y(N,{children:L(e,p(N,"ROOT"))}))(i(e));return t?t.convert(r):r}};const k=a,{transpile:d}=x;var g=e({convertXML:function(e,t){return d(e,t||k)},createAST:function(e){return d(e)}});module.exports=g;
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Nir Moav
|
||||
|
||||
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.
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"name": "simple-xml-to-json",
|
||||
"description": "Convert XML to JSON - Fast & Simple",
|
||||
"keywords": [
|
||||
"XML",
|
||||
"xml",
|
||||
"JSON",
|
||||
"json",
|
||||
"js",
|
||||
"xml2json",
|
||||
"xml2js",
|
||||
"convertor",
|
||||
"conversion",
|
||||
"parsing",
|
||||
"parser",
|
||||
"AST",
|
||||
"ast",
|
||||
"simple",
|
||||
"fast",
|
||||
"lightweight",
|
||||
"small"
|
||||
],
|
||||
"homepage": "https://github.com/nirgit/simple-xml-to-json",
|
||||
"license": "MIT",
|
||||
"version": "1.2.3",
|
||||
"author": "Nir Moav <getnirm@gmail.com>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Nir Moav",
|
||||
"email": "getnirm@gmail.com",
|
||||
"url": "https://nirgit.github.io/"
|
||||
}
|
||||
],
|
||||
"main": "lib/simpleXmlToJson.min.js",
|
||||
"typings": "typings/simple-xml-to-json.d.ts",
|
||||
"files": [
|
||||
"lib",
|
||||
"typings"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "npm run clean && rollup -c --inline",
|
||||
"clean": "rm -rf ./lib/ && mkdir lib ",
|
||||
"lint": "eslint ./src ./test",
|
||||
"prettify": "prettier --write ./**/*.js",
|
||||
"example": "node example/example.js",
|
||||
"test": "jest ",
|
||||
"make-new-release-lib": "npm run validate-lib-health && sh ./scripts/make-new-release.sh",
|
||||
"validate-lib-health": "npm run prettify && npm run lint && npm test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nirgit/simple-xml-to-json.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/nirgit/simple-xml-to-json/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-replace": "^5.0.5",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"acorn": "^8.11.3",
|
||||
"acorn-walk": "^8.3.2",
|
||||
"eslint": "^9.1.1",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-jest": "^28.3.0",
|
||||
"globals": "^15.1.0",
|
||||
"jest": "^29.7.0",
|
||||
"magic-string": "^0.30.10",
|
||||
"prettier": "3.2.5",
|
||||
"rollup": "^4.17.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.12.2"
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
declare module 'simple-xml-to-json' {
|
||||
interface ASTConverter {
|
||||
convert: (astAsJson: object) => any;
|
||||
}
|
||||
|
||||
export function convertXML(xmlAsString: string, customConverter?: ASTConverter): any;
|
||||
|
||||
export function createAST(xmlAsString: string): object;
|
||||
}
|
||||
Reference in new issue
Block a user