diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 3559905392..532e0404e2 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -324,6 +324,7 @@ All definitions files include a header with the author and editors, so at some p
* [WinJS](http://msdn.microsoft.com/en-us/library/windows/apps/br229773.aspx) (from TypeScript samples)
* [WinRT](http://msdn.microsoft.com/en-us/library/windows/apps/br211377.aspx) (from TypeScript samples)
* [ws](http://einaros.github.io/ws/) (by [Paul Loyd](https://github.com/loyd))
+* [x2js](https://code.google.com/p/x2js/) (by [Hiroki Horiuchi](https://github.com/horiuchi/))
* [XRegExp](http://xregexp.com/) (by [Bart van der Schoor](https://github.com/Bartvds))
* [YouTube](https://developers.google.com/youtube/) (by [Daz Wilkin](https://github.com/DazWilkin/))
* [YouTube Analytics API](https://developers.google.com/youtube/analytics/) (by [Frank M](https://github.com/sgtfrankieboy))
diff --git a/x2js/xml2json-tests.ts b/x2js/xml2json-tests.ts
new file mode 100644
index 0000000000..d5a8568e8a
--- /dev/null
+++ b/x2js/xml2json-tests.ts
@@ -0,0 +1,300 @@
+///
+
+// Create x2js instance with default config
+var x2js = new X2JS();
+
+// JSON to DOM
+var xmlDoc = x2js.json2xml(
+ {
+ MyRoot: {
+ MyChild: 'my_child_value',
+ MyAnotherChild: 10,
+ MyArray: [ 'test', 'test2' ],
+ MyArrayRecords: [
+ {
+ ttt: 'vvvv'
+ },
+ {
+ ttt: 'vvvv2'
+ }
+ ]
+ }
+ }
+);
+
+
+// JSON to XML string
+var xmlDocStr = x2js.json2xml_str(
+ {
+ MyRoot: {
+ MyChild: 'my_child_value',
+ MyAnotherChild: 10,
+ MyArray: [ 'test', 'test2' ],
+ MyArrayRecords: [
+ {
+ ttt: 'vvvv'
+ },
+ {
+ ttt: 'vvvv2'
+ }
+ ]
+ }
+ }
+);
+
+console.log(xmlDocStr);
+
+// JSON arrays to string
+var xmlDocStr = x2js.json2xml_str(
+ {
+ MyRoot: {
+ namedItemArray: {
+ item: [
+ { first: 'success1' } ,
+ { first: 'success2' }
+ ]
+ },
+ namedArray: [
+ { first: 'success1' } ,
+ { first: 'success2' }
+ ],
+ justArray: [ 'just success1', 'just success2' ],
+ arrayWithAttrs: [
+ {
+ _test: 'successAttr',
+ __text: 'success',
+ temp: 'successTemp'
+ },
+ {
+ _test: 'successAttr2',
+ __text: 'success2',
+ temp: 'successTemp2'
+ }
+ ]
+ }
+ }
+);
+
+console.log(xmlDocStr);
+
+// XML string to JSON
+var xmlText = "Success- ddsfg
- dsdgfdgfd
";
+var jsonObj = x2js.xml_str2json(xmlText);
+console.log(jsonObj.MyOperation.test);
+
+// Array access form examples
+console.log(x2js.asArray(jsonObj.MyOperation.test)[0]);
+// Or old style (1.0.+):
+var x2jsOld = new X2JS({arrayAccessForm: "property"});
+jsonObj = x2jsOld.xml_str2json(xmlText);
+console.log("Old is " + jsonObj.MyOperation.test_asArray[0]);
+
+// XML/DOM to JSON
+var xmlText = " - Success - TestText - ddsfg
TestText2 - dsdgfdgfd
"
+xmlDoc = x2js.parseXmlString(xmlText);
+
+var jsonObj = x2js.xml2json(xmlDoc);
+console.log(jsonObj.MyOperation.test);
+
+// Parsing XML attrs
+var xmlText = "SUCCESS TXTSuccess- ddsfg
- dsdgfdgfd
";
+var jsonObj = x2js.xml_str2json(xmlText);
+console.log(jsonObj.MyOperation._myAttr);
+console.log(jsonObj.MyOperation.test2._myAttr);
+console.log(jsonObj.MyOperation.txtAttrChild._sAttr);
+console.log(jsonObj.MyOperation.txtAttrChild.__text);
+console.log(jsonObj.MyOperation.txtAttrChild.toString());
+
+// JSON to XML attrs
+var xmlDocStr = x2js.json2xml_str(
+ {
+ TestAttrRoot: {
+ _myAttr: 'myAttrValue',
+ MyChild: 'my_child_value',
+ MyAnotherChild: 10,
+ MyTextAttrChild: {
+ _myTextAttr: 'myTextAttrValue',
+ __text: 'HelloText'
+ }
+ }
+ }
+);
+
+console.log(xmlDocStr);
+
+//Change prefix for attributes
+var x2jsChangedAttrs = new X2JS({
+ // XML attributes. Default is "_"
+ attributePrefix: "$"
+});
+jsonObj = x2jsChangedAttrs.xml_str2json(xmlText);
+console.log(jsonObj.MyOperation.$myAttr);
+console.log(jsonObj.MyOperation.test2.$myAttr);
+console.log(jsonObj.MyOperation.txtAttrChild.$sAttr);
+
+xmlDocStr = x2jsChangedAttrs.json2xml_str({
+ TestAttrRoot: {
+ _myAttr: 'myAttrValue',
+ MyChild: 'my_child_value',
+ MyAnotherChild: 10,
+ MyTextAttrChild: {
+ $myTextAttr: 'myTextAttrValue',
+ __text: 'HelloText'
+ }
+ }
+ }
+);
+
+console.log(xmlDocStr);
+
+
+// Parse XML with namespaces
+var xmlText = "Success- ddsfg
- dsdgfdgfd
testArrSize";
+var jsonObj = x2js.xml_str2json(xmlText);
+console.log(jsonObj.MyOperation.test);
+if (jsonObj.MyOperation.test2.item.length > 2)
+ console.log("Error! Incorrect array len!");
+
+var testObjC = {
+ 'm:TestAttrRoot': {
+ '_tns:m': 'http://www.example.org',
+ '_tns:cms': 'http://www.example.org',
+ MyChild: 'my_child_value',
+ 'cms:MyAnotherChild': 'vdfd'
+ }
+}
+
+// Parse JSON object with namespaces
+var xmlDocStr = x2js.json2xml_str(
+ testObjC
+);
+
+console.log(xmlDocStr);
+
+// Parse JSON object constructed with another NS-style
+var testObjNew = {
+ TestAttrRoot: {
+ __prefix: 'm',
+ '_tns:m': 'http://www.example.org',
+ '_tns:cms': 'http://www.example.org',
+ MyChild: 'my_child_value',
+ MyAnotherChild: {
+ __prefix: 'cms',
+ __text: 'vdfd'
+ }
+ }
+}
+
+// Parse JSON object with namespaces
+var xmlDocStr = x2js.json2xml_str(
+ testObjNew
+);
+
+console.log(xmlDocStr);
+
+// Parse XML with header
+var xmlText = "\n" +
+ "XML HEADER SUCCESS!";
+
+var jsonObj = x2js.xml_str2json(xmlText);
+console.log(jsonObj.test);
+
+// Parse XML with CDATA
+var xmlText = "simple success]]> ";
+
+var jsonObj = x2js.xml_str2json(xmlText);
+console.log(jsonObj.test.data.toString());
+console.log(jsonObj.test.data.__cdata);
+console.log(jsonObj.test.simple);
+
+
+// Parse JSON object with CDATA
+var xmlDocStr = x2js.json2xml_str(
+ jsonObj
+);
+console.log(xmlDocStr);
+
+// Parse JSON with emtpy attributes
+var xmlDocStr = x2js.json2xml_str(
+ {
+ MyRoot: {
+ MyNullChild: null,
+ MyNullChild2: undefined,
+ MyAnotherChild: 10,
+ MyEmptyChild: {
+ _attr: "test"
+ },
+ MyEmptyChild2: {
+ _attr: "test",
+ __text: "Empty Nodes Test"
+ }
+ }
+ }
+);
+
+console.log(xmlDocStr);
+
+// Escaping XML characters
+xmlDocStr = x2js.json2xml_str(
+ {
+ MyRoot: {
+ MyEscapeXmlChild: " & \" ' / ",
+ MyEscapeXmlChild2: {
+ _attr: "success",
+ __text: " & \" ' / "
+ },
+ MyEscapeXmlChildNonString: false
+ }
+ }
+);
+
+console.log(xmlDocStr);
+
+jsonObj = x2js.xml_str2json(xmlDocStr);
+console.log(jsonObj.MyRoot.MyEscapeXmlChild);
+console.log(jsonObj.MyRoot.MyEscapeXmlChild2.toString());
+
+console.log(x2js.getVersion());
+
+// Array access path demos
+x2js = new X2JS({
+ arrayAccessFormPaths: [
+ "MyArrays.test4.item",
+ /.*\.test3\.item/
+ ]
+});
+
+xmlText = "" +
+ "- success
- second
" +
+ "- success
" +
+ "- success
" +
+ "- success
" +
+ "";
+
+
+jsonObj = x2js.xml_str2json(xmlText);
+console.log(jsonObj.MyArrays.test3.item[0]);
+console.log(jsonObj.MyArrays.test4.item[0]);
+console.log(jsonObj.MyArrays.test5.item);
+
+// Working with datetimes
+x2js = new X2JS({
+ datetimeAccessFormPaths: [
+ "MyDts.testds",
+ /.*\.testdt.*/
+ ]
+});
+
+xmlText = "" +
+ "2002-10-10T12:00:00+04:00" +
+ "2002-10-10T12:00:00Z" +
+ "2002-10-10T12:00:00" +
+ "2002-10-10T12:00:00Z" +
+ "";
+jsonObj = x2js.xml_str2json(xmlText);
+
+console.log(jsonObj.MyDts.testds);
+console.log(jsonObj.MyDts.testdt1);
+console.log(jsonObj.MyDts.testdt2);
+console.log(x2js.asDateTime(jsonObj.MyDts.testdc));
+
diff --git a/x2js/xml2json.d.ts b/x2js/xml2json.d.ts
new file mode 100644
index 0000000000..c202eface6
--- /dev/null
+++ b/x2js/xml2json.d.ts
@@ -0,0 +1,32 @@
+
+interface IX2JS {
+ new (config?: IX2JSOption): IX2JS;
+
+ getVersion(): string;
+
+ xml2json(dom: Node): T;
+ json2xml(json: T): Node;
+ xml_str2json(xml: string): T;
+ json2xml_str(json: T): string;
+ parseXmlString(xml: string): Node;
+
+ asArray(prop: any): any[];
+ asDateTime(key: string): string;
+ asXmlDateTime(date: Date): string;
+ asXmlDateTime(date: number): string;
+}
+
+interface IX2JSOption {
+ escapeMode?: boolean;
+ attributePrefix?: string;
+ arrayAccessForm?: string;
+ emptyNodeForm?: string;
+ enableToStringFunc?: boolean;
+ arrayAccessFormPaths?: any[];
+ skipEmptyTextNodesForObj?: boolean;
+ stripWhitespaces?: boolean;
+ datetimeAccessFormPaths?: any[];
+}
+
+declare var X2JS: IX2JS;
+