Examples of good XML to JSON JavaScript Libraries? [duplicate] - javascript

How would you convert from XML to JSON and then back to XML?
The following tools work quite well, but aren't completely consistent:
xml2json
Has anyone encountered this situation before?

I think this is the best one: Converting between XML and JSON
Be sure to read the accompanying article on the xml.com O'Reilly site, which goes into details of the problems with these conversions, which I think you will find enlightening. The fact that O'Reilly is hosting the article should indicate that Stefan's solution has merit.

https://github.com/abdmob/x2js - my own library (updated URL from http://code.google.com/p/x2js/):
This library provides XML to JSON (JavaScript Objects) and vice versa javascript conversion functions. The library is very small and doesn't require any other additional libraries.
API functions
new X2JS() - to create your instance to access all library functionality. Also you could specify optional configuration options here
X2JS.xml2json - Convert XML specified as DOM Object to JSON
X2JS.json2xml - Convert JSON to XML DOM Object
X2JS.xml_str2json - Convert XML specified as string to JSON
X2JS.json2xml_str - Convert JSON to XML string
Online Demo on http://jsfiddle.net/abdmob/gkxucxrj/1/
var x2js = new X2JS();
function convertXml2JSon() {
$("#jsonArea").val(JSON.stringify(x2js.xml_str2json($("#xmlArea").val())));
}
function convertJSon2XML() {
$("#xmlArea").val(x2js.json2xml_str($.parseJSON($("#jsonArea").val())));
}
convertXml2JSon();
convertJSon2XML();
$("#convertToJsonBtn").click(convertXml2JSon);
$("#convertToXmlBtn").click(convertJSon2XML);

These answers helped me a lot to make this function:
function xml2json(xml) {
try {
var obj = {};
if (xml.children.length > 0) {
for (var i = 0; i < xml.children.length; i++) {
var item = xml.children.item(i);
var nodeName = item.nodeName;
if (typeof (obj[nodeName]) == "undefined") {
obj[nodeName] = xml2json(item);
} else {
if (typeof (obj[nodeName].push) == "undefined") {
var old = obj[nodeName];
obj[nodeName] = [];
obj[nodeName].push(old);
}
obj[nodeName].push(xml2json(item));
}
}
} else {
obj = xml.textContent;
}
return obj;
} catch (e) {
console.log(e.message);
}
}
As long as you pass in a jquery dom/xml object: for me it was:
Jquery(this).find('content').eq(0)[0]
where content was the field I was storing my xml in.

I've created a recursive function based on regex, in case you don't want to install library and understand the logic behind what's happening:
const xmlSample = '<tag>tag content</tag><tag2>another content</tag2><tag3><insideTag>inside content</insideTag><emptyTag /></tag3>';
console.log(parseXmlToJson(xmlSample));
function parseXmlToJson(xml) {
const json = {};
for (const res of xml.matchAll(/(?:<(\w*)(?:\s[^>]*)*>)((?:(?!<\1).)*)(?:<\/\1>)|<(\w*)(?:\s*)*\/>/gm)) {
const key = res[1] || res[3];
const value = res[2] && parseXmlToJson(res[2]);
json[key] = ((value && Object.keys(value).length) ? value : res[2]) || null;
}
return json;
}
Regex explanation for each loop:
res[0] - return the xml (as is)
res[1] - return the xml tag name
res[2] - return the xml content
res[3] - return the xml tag name in case the tag closes itself. In example: <tag />
You can check how the regex works here:
https://regex101.com/r/ZJpCAL/1
Note: In case json has a key with an undefined value, it is being removed.
That's why I've inserted null at the end of line 9.

I was using xmlToJson just to get a single value of the xml.
I found doing the following is much easier (if the xml only occurs once..)
let xml =
'<person>' +
' <id>762384324</id>' +
' <firstname>Hank</firstname> ' +
' <lastname>Stone</lastname>' +
'</person>';
let getXmlValue = function(str, key) {
return str.substring(
str.lastIndexOf('<' + key + '>') + ('<' + key + '>').length,
str.lastIndexOf('</' + key + '>')
);
}
alert(getXmlValue(xml, 'firstname')); // gives back Hank

You can also use txml. It can parse into a DOM made of simple objects and stringify. In the result, the content will be trimmed. So formating of the original with whitespaces will be lost. But this could be used very good to minify HTML.
const xml = require('txml');
const data = `
<tag>tag content</tag>
<tag2>another content</tag2>
<tag3>
<insideTag>inside content</insideTag>
<emptyTag />
</tag3>`;
const dom = xml(data); // the dom can be JSON.stringified
xml.stringify(dom); // this will return the dom into an xml-string
Disclaimer: I am the author of txml, the fastest xml parser in javascript.

A while back I wrote this tool https://bitbucket.org/surenrao/xml2json for my TV Watchlist app, hope this helps too.
Synopsys: A library to not only convert xml to json, but is also easy to debug (without circular errors) and recreate json back to xml. Features :- Parse xml to json object. Print json object back to xml. Can be used to save xml in IndexedDB as X2J objects. Print json object.

In 6 simple ES6 lines:
xml2json = xml => {
var el = xml.nodeType === 9 ? xml.documentElement : xml
var h = {name: el.nodeName}
h.content = Array.from(el.childNodes || []).filter(e => e.nodeType === 3).map(e => e.textContent).join('').trim()
h.attributes = Array.from(el.attributes || []).filter(a => a).reduce((h, a) => { h[a.name] = a.value; return h }, {})
h.children = Array.from(el.childNodes || []).filter(e => e.nodeType === 1).map(c => h[c.nodeName] = xml2json(c))
return h
}
Test with echo "xml2json_example()" | node -r xml2json.es6 with source at https://github.com/brauliobo/biochemical-db/blob/master/lib/xml2json.es6

Disclaimer: I've written fast-xml-parser
Fast XML Parser can help to convert XML to JSON and vice versa. Here is the example;
var options = {
attributeNamePrefix : "#_",
attrNodeName: "attr", //default is 'false'
textNodeName : "#text",
ignoreAttributes : true,
ignoreNameSpace : false,
allowBooleanAttributes : false,
parseNodeValue : true,
parseAttributeValue : false,
trimValues: true,
decodeHTMLchar: false,
cdataTagName: "__cdata", //default is 'false'
cdataPositionChar: "\\c",
};
if(parser.validate(xmlData)=== true){//optional
var jsonObj = parser.parse(xmlData,options);
}
If you want to parse JSON or JS object into XML then
//default options need not to set
var defaultOptions = {
attributeNamePrefix : "#_",
attrNodeName: "#", //default is false
textNodeName : "#text",
ignoreAttributes : true,
encodeHTMLchar: false,
cdataTagName: "__cdata", //default is false
cdataPositionChar: "\\c",
format: false,
indentBy: " ",
supressEmptyNode: false
};
var parser = new parser.j2xParser(defaultOptions);
var xml = parser.parse(json_or_js_obj);

Here' a good tool from a documented and very famous npm library that does the xml <-> js conversions very well: differently from some (maybe all) of the above proposed solutions, it converts xml comments also.
var obj = {name: "Super", Surname: "Man", age: 23};
var builder = new xml2js.Builder();
var xml = builder.buildObject(obj);

I would personally recommend this tool. It is an XML to JSON converter.
It is very lightweight and is in pure JavaScript. It needs no dependencies. You can simply add the functions to your code and use it as you wish.
It also takes the XML attributes into considerations.
var xml = ‘<person id=”1234” age=”30”><name>John Doe</name></person>’;
var json = xml2json(xml);
console.log(json);
// prints ‘{“person”: {“id”: “1234”, “age”: “30”, “name”: “John Doe”}}’
Here's an online demo!

There is an open sourced library Xml-to-json with methods jsonToXml(json) and xmlToJson(xml).
Here's an online demo!

This function directly reads the DOM properties of the XMLDocument (or document node/element) to build the JSON completely and accurately without trying to guess or match. Pass it responseXML, not responseText from XMLHttpRequest.
xml2json(xmlDoc)
If you only have a string of XML and not an XMLDocument, jQuery will convert your text to one.
xml2json($(xmlString)[0])
Each node becomes an object. (All elements are nodes, not all nodes are elements (e.g. text within an element).)
Every object contains the node name and type.
If it has attributes, they appear as properties in an attributes object.
If it has children, they appear recursively as node->objects in a children array.
If it's a Text, CDATA, or Comment node (bare text between element tags) or a comment, it shouldn't have attributes or children but the text will be in a text property.
{
// Always present
"name": "FancyElement",
"type": "Element",
// If present
"attributes: {
"attr1": "val1",
"attr2": "val2"
},
"children": [...],
"text": "buncha fancy words"
}
Caveat: I'm not familiar with all the node types. It's probably not grabbing needed/useful info from all of them. It was tested on and behaves as expected for
Element
Text
CDATA
Comment
Document
function xml2json(xml) {
try {
const types = [null,
"Element",
"Attribute",
"Text",
"CDATA",
"EntityReference", // Deprecated
"Entity", // Deprecated
"ProcessingInstruction",
"Comment",
"Document",
"DocumentType",
"DocumentFragment",
"Notation" // Deprecated
];
var o = {};
o.name = xml.nodeName;
o.type = types[xml.nodeType];
if (xml.nodeType == 3 ||
xml.nodeType == 4 ||
xml.nodeType == 8 ) {
o.text = xml.textContent;
} else {
if (xml.attributes) {
o.attributes = {};
for (const a of xml.attributes) {
o.attributes[a.name] = a.value;
}
}
if (xml.childNodes.length) {
o.children = [];
for (const x of xml.childNodes) {
o.children.push(xml2json(x))
}
}
}
return (o);
} catch (e) {
alert('Error in xml2json. See console for details.');
console.log('Error in xml2json processing node:');
console.log(o);
console.log('Error:');
console.log(e);
}
}
var doc = document.getElementById("doc");
var out = document.getElementById("out");
out.innerText = JSON.stringify(xml2json(doc), null, 2);
/* Let's process the whole Code Snippet #document, why not?
* Yes, the JSON we just put in the document body and all
* this code is encoded in the JSON in the console.
* In that copy you can see why the XML DOM will all be one line.
* The JSON in the console has "\n" nodes all throughout.
*/
console.log(xml2json(document));
#doc,
#out {
border: 1px solid black;
}
<div id="doc"><!-- The XML DOM will all be on one line --><div personality="bubbly" relevance=42>This text is valid for HTML.<span>But it probably shouldn't be siblings to an element in XML.</span></div></div>
<pre id="out"></pre>

The best way to do it using server side as client side doesn't work well in all scenarios. I was trying to build online json to xml and xml to json converter using javascript and I felt almost impossible as it was not working in all scenarios. Ultimately I ended up doing it server side using Newtonsoft in ASP.MVC. Here is the online converter http://techfunda.com/Tools/XmlToJson

Related

how to filter in node.js for getting json object value which has xml data [duplicate]

How would you convert from XML to JSON and then back to XML?
The following tools work quite well, but aren't completely consistent:
xml2json
Has anyone encountered this situation before?
I think this is the best one: Converting between XML and JSON
Be sure to read the accompanying article on the xml.com O'Reilly site, which goes into details of the problems with these conversions, which I think you will find enlightening. The fact that O'Reilly is hosting the article should indicate that Stefan's solution has merit.
https://github.com/abdmob/x2js - my own library (updated URL from http://code.google.com/p/x2js/):
This library provides XML to JSON (JavaScript Objects) and vice versa javascript conversion functions. The library is very small and doesn't require any other additional libraries.
API functions
new X2JS() - to create your instance to access all library functionality. Also you could specify optional configuration options here
X2JS.xml2json - Convert XML specified as DOM Object to JSON
X2JS.json2xml - Convert JSON to XML DOM Object
X2JS.xml_str2json - Convert XML specified as string to JSON
X2JS.json2xml_str - Convert JSON to XML string
Online Demo on http://jsfiddle.net/abdmob/gkxucxrj/1/
var x2js = new X2JS();
function convertXml2JSon() {
$("#jsonArea").val(JSON.stringify(x2js.xml_str2json($("#xmlArea").val())));
}
function convertJSon2XML() {
$("#xmlArea").val(x2js.json2xml_str($.parseJSON($("#jsonArea").val())));
}
convertXml2JSon();
convertJSon2XML();
$("#convertToJsonBtn").click(convertXml2JSon);
$("#convertToXmlBtn").click(convertJSon2XML);
These answers helped me a lot to make this function:
function xml2json(xml) {
try {
var obj = {};
if (xml.children.length > 0) {
for (var i = 0; i < xml.children.length; i++) {
var item = xml.children.item(i);
var nodeName = item.nodeName;
if (typeof (obj[nodeName]) == "undefined") {
obj[nodeName] = xml2json(item);
} else {
if (typeof (obj[nodeName].push) == "undefined") {
var old = obj[nodeName];
obj[nodeName] = [];
obj[nodeName].push(old);
}
obj[nodeName].push(xml2json(item));
}
}
} else {
obj = xml.textContent;
}
return obj;
} catch (e) {
console.log(e.message);
}
}
As long as you pass in a jquery dom/xml object: for me it was:
Jquery(this).find('content').eq(0)[0]
where content was the field I was storing my xml in.
I've created a recursive function based on regex, in case you don't want to install library and understand the logic behind what's happening:
const xmlSample = '<tag>tag content</tag><tag2>another content</tag2><tag3><insideTag>inside content</insideTag><emptyTag /></tag3>';
console.log(parseXmlToJson(xmlSample));
function parseXmlToJson(xml) {
const json = {};
for (const res of xml.matchAll(/(?:<(\w*)(?:\s[^>]*)*>)((?:(?!<\1).)*)(?:<\/\1>)|<(\w*)(?:\s*)*\/>/gm)) {
const key = res[1] || res[3];
const value = res[2] && parseXmlToJson(res[2]);
json[key] = ((value && Object.keys(value).length) ? value : res[2]) || null;
}
return json;
}
Regex explanation for each loop:
res[0] - return the xml (as is)
res[1] - return the xml tag name
res[2] - return the xml content
res[3] - return the xml tag name in case the tag closes itself. In example: <tag />
You can check how the regex works here:
https://regex101.com/r/ZJpCAL/1
Note: In case json has a key with an undefined value, it is being removed.
That's why I've inserted null at the end of line 9.
I was using xmlToJson just to get a single value of the xml.
I found doing the following is much easier (if the xml only occurs once..)
let xml =
'<person>' +
' <id>762384324</id>' +
' <firstname>Hank</firstname> ' +
' <lastname>Stone</lastname>' +
'</person>';
let getXmlValue = function(str, key) {
return str.substring(
str.lastIndexOf('<' + key + '>') + ('<' + key + '>').length,
str.lastIndexOf('</' + key + '>')
);
}
alert(getXmlValue(xml, 'firstname')); // gives back Hank
You can also use txml. It can parse into a DOM made of simple objects and stringify. In the result, the content will be trimmed. So formating of the original with whitespaces will be lost. But this could be used very good to minify HTML.
const xml = require('txml');
const data = `
<tag>tag content</tag>
<tag2>another content</tag2>
<tag3>
<insideTag>inside content</insideTag>
<emptyTag />
</tag3>`;
const dom = xml(data); // the dom can be JSON.stringified
xml.stringify(dom); // this will return the dom into an xml-string
Disclaimer: I am the author of txml, the fastest xml parser in javascript.
A while back I wrote this tool https://bitbucket.org/surenrao/xml2json for my TV Watchlist app, hope this helps too.
Synopsys: A library to not only convert xml to json, but is also easy to debug (without circular errors) and recreate json back to xml. Features :- Parse xml to json object. Print json object back to xml. Can be used to save xml in IndexedDB as X2J objects. Print json object.
Disclaimer: I've written fast-xml-parser
Fast XML Parser can help to convert XML to JSON and vice versa. Here is the example;
var options = {
attributeNamePrefix : "#_",
attrNodeName: "attr", //default is 'false'
textNodeName : "#text",
ignoreAttributes : true,
ignoreNameSpace : false,
allowBooleanAttributes : false,
parseNodeValue : true,
parseAttributeValue : false,
trimValues: true,
decodeHTMLchar: false,
cdataTagName: "__cdata", //default is 'false'
cdataPositionChar: "\\c",
};
if(parser.validate(xmlData)=== true){//optional
var jsonObj = parser.parse(xmlData,options);
}
If you want to parse JSON or JS object into XML then
//default options need not to set
var defaultOptions = {
attributeNamePrefix : "#_",
attrNodeName: "#", //default is false
textNodeName : "#text",
ignoreAttributes : true,
encodeHTMLchar: false,
cdataTagName: "__cdata", //default is false
cdataPositionChar: "\\c",
format: false,
indentBy: " ",
supressEmptyNode: false
};
var parser = new parser.j2xParser(defaultOptions);
var xml = parser.parse(json_or_js_obj);
Here' a good tool from a documented and very famous npm library that does the xml <-> js conversions very well: differently from some (maybe all) of the above proposed solutions, it converts xml comments also.
var obj = {name: "Super", Surname: "Man", age: 23};
var builder = new xml2js.Builder();
var xml = builder.buildObject(obj);
In 6 simple ES6 lines:
xml2json = xml => {
var el = xml.nodeType === 9 ? xml.documentElement : xml
var h = {name: el.nodeName}
h.content = Array.from(el.childNodes || []).filter(e => e.nodeType === 3).map(e => e.textContent).join('').trim()
h.attributes = Array.from(el.attributes || []).filter(a => a).reduce((h, a) => { h[a.name] = a.value; return h }, {})
h.children = Array.from(el.childNodes || []).filter(e => e.nodeType === 1).map(c => h[c.nodeName] = xml2json(c))
return h
}
Test with echo "xml2json_example()" | node -r xml2json.es6 with source at https://github.com/brauliobo/biochemical-db/blob/master/lib/xml2json.es6
I would personally recommend this tool. It is an XML to JSON converter.
It is very lightweight and is in pure JavaScript. It needs no dependencies. You can simply add the functions to your code and use it as you wish.
It also takes the XML attributes into considerations.
var xml = ‘<person id=”1234” age=”30”><name>John Doe</name></person>’;
var json = xml2json(xml);
console.log(json);
// prints ‘{“person”: {“id”: “1234”, “age”: “30”, “name”: “John Doe”}}’
Here's an online demo!
There is an open sourced library Xml-to-json with methods jsonToXml(json) and xmlToJson(xml).
Here's an online demo!
This function directly reads the DOM properties of the XMLDocument (or document node/element) to build the JSON completely and accurately without trying to guess or match. Pass it responseXML, not responseText from XMLHttpRequest.
xml2json(xmlDoc)
If you only have a string of XML and not an XMLDocument, jQuery will convert your text to one.
xml2json($(xmlString)[0])
Each node becomes an object. (All elements are nodes, not all nodes are elements (e.g. text within an element).)
Every object contains the node name and type.
If it has attributes, they appear as properties in an attributes object.
If it has children, they appear recursively as node->objects in a children array.
If it's a Text, CDATA, or Comment node (bare text between element tags) or a comment, it shouldn't have attributes or children but the text will be in a text property.
{
// Always present
"name": "FancyElement",
"type": "Element",
// If present
"attributes: {
"attr1": "val1",
"attr2": "val2"
},
"children": [...],
"text": "buncha fancy words"
}
Caveat: I'm not familiar with all the node types. It's probably not grabbing needed/useful info from all of them. It was tested on and behaves as expected for
Element
Text
CDATA
Comment
Document
function xml2json(xml) {
try {
const types = [null,
"Element",
"Attribute",
"Text",
"CDATA",
"EntityReference", // Deprecated
"Entity", // Deprecated
"ProcessingInstruction",
"Comment",
"Document",
"DocumentType",
"DocumentFragment",
"Notation" // Deprecated
];
var o = {};
o.name = xml.nodeName;
o.type = types[xml.nodeType];
if (xml.nodeType == 3 ||
xml.nodeType == 4 ||
xml.nodeType == 8 ) {
o.text = xml.textContent;
} else {
if (xml.attributes) {
o.attributes = {};
for (const a of xml.attributes) {
o.attributes[a.name] = a.value;
}
}
if (xml.childNodes.length) {
o.children = [];
for (const x of xml.childNodes) {
o.children.push(xml2json(x))
}
}
}
return (o);
} catch (e) {
alert('Error in xml2json. See console for details.');
console.log('Error in xml2json processing node:');
console.log(o);
console.log('Error:');
console.log(e);
}
}
var doc = document.getElementById("doc");
var out = document.getElementById("out");
out.innerText = JSON.stringify(xml2json(doc), null, 2);
/* Let's process the whole Code Snippet #document, why not?
* Yes, the JSON we just put in the document body and all
* this code is encoded in the JSON in the console.
* In that copy you can see why the XML DOM will all be one line.
* The JSON in the console has "\n" nodes all throughout.
*/
console.log(xml2json(document));
#doc,
#out {
border: 1px solid black;
}
<div id="doc"><!-- The XML DOM will all be on one line --><div personality="bubbly" relevance=42>This text is valid for HTML.<span>But it probably shouldn't be siblings to an element in XML.</span></div></div>
<pre id="out"></pre>
The best way to do it using server side as client side doesn't work well in all scenarios. I was trying to build online json to xml and xml to json converter using javascript and I felt almost impossible as it was not working in all scenarios. Ultimately I ended up doing it server side using Newtonsoft in ASP.MVC. Here is the online converter http://techfunda.com/Tools/XmlToJson

How can I reformat this simple JSON so it doesn't catch "Circular structure to JSON" exception?

Introduction
I'm learning JavaScript on my own and JSON its something along the path. I'm working on a JavaScript WebScraper and I want, for now, load my results in JSON format.
I know I can use data base, server-client stuff, etc to work with data. But I want to take this approach as learning JSON and how to parse/create/format it's my main goal for today.
Explaining variables
As you may have guessed the data stored in the fore mentioned variables comes from an html file. So an example of the content in:
users[] -> "Egypt"
GDP[] -> "<td> $2,971</td>"
Regions[] -> "<td> Egypt </td>"
Align[] -> "<td> Eastern Bloc </td>"
Code
let countries = [];
for(let i = 0; i < users.length; i++)
{
countries.push( {
'country' : [{
'name' : users[i],
'GDP' : GDP[i],
'Region' : regions[i],
'Align' : align[i]
}]})
};
let obj_data = JSON.stringify(countries, null, 2);
fs.writeFileSync('countryballs.json', obj_data);
Code explanation
I have previously loaded into arrays (users, GDP, regionsm align) those store the data (String format) I had extracted from a website.
My idea was to then "dump" it into an object with which the stringify() function format would format it into JSON.
I have tested it without the loop (static data just for testing) and it works.
Type of error
let obj_data = JSON.stringify(countries, null, 2);
^
TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Node'
| property 'children' -> object with constructor 'Array'
| index 0 -> object with constructor 'Node'
--- property 'parent' closes the circle
What I want from this question
I want to know what makes this JSON format "Circular" and how to make this code work for my goals.
Notes
I am working with Node.js and Visual Studio Code
EDIT
This is further explanation for those who were interested and thought it was not a good question.
Test code that works
let countries;
console.log(users.length)
for(let i = 0; i < users.length; i++)
{
countries = {
country : [
{
"name" : 'CountryTest'
}
]
}
};
let obj_data = JSON.stringify(countries, null, 2);
fs.writeFileSync('countryballs.json', obj_data);
});
Notice in comparison to the previous code, right now I am inputing "manually" the name of the country object.
This way absolutely works as you can see below:
Now, if I change 'CountryTest' to into a users[i] where I store country names (Forget about why countries are tagged users, it is out of the scope of this question)
It shows me the previous circular error.
A "Partial Solution" for this was to add +"" which, as I said, partially solved the problem as now there is not "Circular Error"
Example:
for(let i = 0; i < users.length; i++)
{
countries = {
country : [
{
"name" : users[i]+''
}
]
}
};
Resulting in:
Another bug, which I do not know why is that only shows 1 country when there are 32 in the array users[]
This makes me think that the answers provided are not correct so far.
Desired JSON format
{
"countries": {
"country": [
{
"name": "",
"GDP" : "",
"Region" : "",
"Align" : ""
},
{
"name": "",
"GDP" : "",
"Region" : "",
"Align" : ""
},
{
"name": "",
"GDP" : "",
"Region" : "",
"Align" : ""
}
]}
}
Circular structure error occurs when you have a property of the object which is the object itself directly (a -> a) or indirectly (a -> b -> a).
To avoid the error message, tell JSON.stringify what to do when it encounters a circular reference. For example, if you have a person pointing to another person ("parent"), which may (or may not) point to the original person, do the following:
JSON.stringify( that.person, function( key, value) {
if( key == 'parent') { return value.id;}
else {return value;}
})
The second parameter to stringify is a filter function. Here it simply converts the referred object to its ID, but you are free to do whatever you like to break the circular reference.
You can test the above code with the following:
function Person( params) {
this.id = params['id'];
this.name = params['name'];
this.father = null;
this.fingers = [];
// etc.
}
var me = new Person({ id: 1, name: 'Luke'});
var him = new Person( { id:2, name: 'Darth Vader'});
me.father = him;
JSON.stringify(me); // so far so good
him.father = me; // time travel assumed :-)
JSON.stringify(me); // "TypeError: Converting circular structure to JSON"
// But this should do the job:
JSON.stringify(me, function( key, value) {
if(key == 'father') {
return value.id;
} else {
return value;
};
})
The answer is from StackOverflow question,
Stringify (convert to JSON) a JavaScript object with circular reference
From your output, it looks as though users is a list of DOM nodes. Rather than referring to these directly (where there are all sort of possible cyclical structures), if you just want their text, instead of using users directly, try something like
country : [
{
"name" : users[i].textContent // maybe also followed by `.trim()
}
]
Or you could do this up front to your whole list:
const usersText = [...users].map(node => node.textContent)
and then use usersText in place of users as you build your object.
If GDP, regions and align are also references to your HTML, then you might have to do the same with them.
EUREKA!
As some of you have mentioned above, let me tell you it is not a problem of circularity, at first..., in the JSON design. It is an error of the data itself.
When I scraped the data it came in html format i.e <td>whatever</td>, I did not care about that as I could simply take it away later. I was way too focused in having the JSON well formatted and learning.
As #VLAZ and #Scott Sauyezt mentioned above, it could be that some of the data, if it is not well formatted into string, it might be referring to itself somehow as so I started to work on that.
Lets have a look at this assumption...
To extract the data I used the cheerio.js which gives you a kind of jquery thing to parse html.
To extract the name of the country I used:
nullTest = ($('table').eq(2).find('tr').eq(i).find('td').find('a').last());
//"Partial solution" for the OutOfIndex nulls
if (nullTest != null)
{
users.push(nullTest);
}
(nullTest helps me avoid nulls, I will implement some RegEx when everything works to polish the code a bit)
This "query" would output me something like:
whatEverIsInHereIfThereIsAny
or else.
to get rid off this html thing just add .html() at the end of the "jquery" such as:
($('table').eq(2).find('tr').eq(i).find('td').find('a').last().html());
That way you are now working with String and avoiding any error and thus solves this question.

Fiddler XML Parsing

FiddlerScript is capable of JSON parsing and modification. For example, the following addition to OnBeforeResponse will sneakily replace Ipify's returned external IP address with 127.0.0.1:
if (oSession.url == "api.ipify.org/?format=json"){
var j = GetResponseJson(oSession);
j["ip"] = "127.0.0.1";
SetResponseJson(oSession, j);
}
where GetResponseJson and SetResponseJson are helper functions I made from Eric's linked answer:
static function GetResponseJson(oSession: Session){
return Fiddler.WebFormats.JSON.JsonDecode(oSession.GetResponseBodyAsString()).JSONObject;
}
static function SetResponseJson(oSession: Session, j){
oSession.utilSetResponseBody(Fiddler.WebFormats.JSON.JsonEncode(j));
}
This is great for modifying JSON payloads intercepted by Fiddler.
My question is:
Is there an equivalent way to parse and modify XML in FiddlerScript?
FiddlerScript uses JScript.NET, and thus can reference .NET assemblies, including System.Xml, which contains the XmlDocument class.
First, in Fiddler > Tools > Fiddler Options > Extensions, add a reference to System.Xml.dll:
Next, at the top of FiddlerScript, reference it:
import System.Xml;
At that point, you can create XmlDocument objects:
var x = new XmlDocument();
To add a custom column from a node in XML in the response, you need to use FiddlerScript.
What is required when you have loaded the System.xml reference, in the FiddlerScript (Rules > Customize Rules), you can add :
public static BindUIColumn("Request")
function testXmlColumn(oSession: Session){
if (oSession.oResponse != null && oSession.oResponse["Content-Type"] == "text/xml; charset=utf-8") {
var doc = new XmlDocument();
doc.LoadXml(oSession.GetResponseBodyAsString());
var xPathString = '/an/XPath/Expression';
var xmlNode = doc.DocumentElement.SelectSingleNode(xPathString);
return xmlNode.InnerText;
}
return "";
}
Obviously, you need to replace /an/XPath/Expression with an XPath expression. This is fairly simple language to match a node.

Accessing a javascript object in D3.js

A javascript data object (JSON notation) has been created with the following content:
"[
{"range":"Shape","values":[{"idx":0,"val":"Random"},{"idx":1,"val":"Line"},{"idx":2,"val":"Square"},{"idx":3,"val":"Circle"},{"idx":4,"val":"Oval"},{"idx":5,"val":"Egg"}]},
{"range":"Color","values":[{"idx":0,"val":"Red"},{"idx":1,"val":"Blue"},{"idx":2,"val":"Yellow"},{"idx":3,"val":"Green"},{"idx":4,"val":"Cyan"}]}
]"
In a next step the index of an ordinal value has to be found in this object. The function should find the index of the value 'Blue' in the range 'Color'.
So the function should have the meta scripting form
f("Color")("Blue")=1
What is the most elegant form to create such a function in the context of D3 and javascript?
Depending on your use case, it might make sense to convert the data structure to a different structure more suitable for direct access. E.g. you could convert your structure to
var data = {
Shape: ['Random', 'Line', ...],
// ...
};
and access it with
data['Shape'].indexOf('Line') // or data.Shape.indexOf('Line')
Or go even one step further and convert to
var data = {
Shape: {
Random: 0,
Line: 1,
// ...
},
// ...
};
and access it with
data['Shape']['Line'] // or data.Shape.Line
What the best solution is depends on the actual use case.
Converting the structure dynamically is pretty straight forward. Here is an example to convert it to the first suggestion:
var newData = {};
data.forEach(function(item) {
newData[item.range] =
item.values.map(function(value) { return value.val; });
});
This would also reduce redundancy (e.g. idx seems to correspond with the element index).
Would this work for you ?
var dataJson = '[ \
{"range":"Shape","values":[{"idx":0,"val":"Random"},{"idx":1,"val":"Line"},{"idx":2,"val":"Square"},{"idx":3,"val":"Circle"},{"idx":4,"val":"Oval"},{"idx":5,"val":"Egg"}]},\
{"range":"Color","values":[{"idx":0,"val":"Red"},{"idx":1,"val":"Blue"},{"idx":2,"val":"Yellow"},{"idx":3,"val":"Green"},{"idx":4,"val":"Cyan"}]}\
]';
var data = JSON.parse(dataJson);
for (each in data){
if ( (data[each].range) === 'Color'){
for (eachVal in data[each].values){
if (data[each].values[eachVal].val === 'Blue'){
alert(data[each].values[eachVal].idx);
}
}
} ;
}
And here is the JSFiddle for you too.

passing formatting JavaScript code to HighCharts with JSON

I have a website that uses AJAX to deliver a JSON formatted string to a HighCharts chart.
You can see this as the middle JSON code part at:
http://jsfiddle.net/1Loag7pv/
$('#container').highcharts(
//JSON Start
{
"plotOptions": {
"series": {"animation": {"duration": 500}}
,"pie": {
"allowPointSelect": true,
"cursor": "pointer",
"dataLabels": {"formatter":function(){return this.point.name+': '+this.percentage.toFixed(1) + '%';}}
}
},
"chart":{"renderTo":"divReportChart"}
,"title":{"text":"Sales Totals"}
,"xAxis":{"title":{"text":"Item"}, "categories":["Taxes","Discounts","NetSalesTotal"], "gridLineWidth":1}
,"yAxis":[{"title":{"text":"Amount"}, "gridLineWidth":1}]
,"series":[{"name":"Amount","type":"pie", "startAngle": -60,"yAxis": 0,"data":[["Taxes",17.8700],["Discounts",36.0000],["NetSalesTotal",377.9500]]}]
}
//JSON end
);
The problem is that the function part...
"dataLabels": {"formatter":function(){return this.point.name+': '+this.percentage.toFixed(1) + '%';}}
is not being transferred via the JSON
All research tells me that there is NO WAY to do this.
IE... Is it valid to define functions in JSON results?
Anybody got an idea on how to get around this limitation?
It is true that you cannot pass functions in JSON. Javascript is a superset of JSON.
A common approach is for the chart to be defined in javascript (e.g. during the page load), and the page then requests just the data via Ajax. When the data is returned it can be added to the chart object, either before it is rendered or afterwards using the highcharts API.
If you really want to pass the formatter function from the server with the chart, send it as a string, and then turn it into a function like this:
var fn = Function(mystring);
and use it in highcharts like:
chart.plotOptions.pie.dataLabels = {"formatter":fn};
I've re-factored your example to show the approach: http://jsfiddle.net/wo7zn0bw/
I had a similar conundrum. I wanted to create the JSON server side (ruby on rails) so I could create images of charts for a web API and also present it on the client web browser with the same code. This is similar to SteveP's answer.
To conform with JSON standards, I changed all formatter functions to strings
{"formatter": "function(){ return this.point.name+':'+this.percentage.toFixed(1) + '%';}"}
On the web side, I navigate the hash looking for formatter keys and replace them with the function using this code (may be a better way!?). javascript:
function HashNavigator(){
this.navigateAndReplace = function(hash, key){
if (!this.isObject(hash)){
//Nice if only navigated hashes and arrays
return;
}
var keys = Object.keys(hash);
for(var i = 0; i< keys.length; i++){
if (keys[i] == key){
//convert string to js function
hash[keys[i]] = this.parseFunction(hash[keys[i]]);
} else if (this.isObject(hash[keys[i]])){
//navigate hash tree
this.navigateAndReplace(hash[keys[i]], key);
} else {
//continue
}
}
};
this.isObject = function(testVar) {
return testVar !== null && typeof testVar === 'object'
}
//http://stackoverflow.com/questions/7650071/is-there-a-way-to-create-a-function-from-a-string-with-javascript
this.parseFunction = function(fstring){
var funcReg = /function *\(([^()]*)\)[ \n\t]*{(.*)}/gmi;
var match = funcReg.exec(fstring.replace(/\n/g, ' '));
if(match) {
return new Function(match[1].split(','), match[2]);
}
return null;
};
}
To use this, would be something similar to this javascript:
hashNavigator = new HashNavigator();
hashNavigator.navigateAndReplace(myHighchartsHash, "formatter")
At that point the hash/js-object is Highcharts ready
Similar idea was used for the web image API.
I was really hoping that hacking at the JSON was not the only solution, but it works!
I used a different approach. I created a JSON like below
{"formatter": "function(){ return this.point.name+':'+this.percentage.toFixed(1) + '%';}"}
When I came to evaluating the expression, I used (assuming that the value of the 'formatter' is formatterValueString)
formatterValueString = formatterValueString.replace('function()', '');
let opts = (new Function(formatterValueString)).call(this);
formatterValue = opts;
The reason to use this approach was it became hard to bind 'this' with the function. The eval() function did not go well with accessing variable this. I am sure there are ways to do it. Just thought this was quick.

Categories

Resources