#region License // Copyright (c) 2007 James Newton-King // // 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. #endregion #if !SILVERLIGHT && !NETFX_CORE using System; using System.Collections.Generic; using Newtonsoft.Json.Tests.Serialization; using Newtonsoft.Json.Tests.TestObjects; #if !NETFX_CORE using NUnit.Framework; #else using Microsoft.VisualStudio.TestTools.UnitTesting; using TestFixture = Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute; using Test = Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute; #endif using Newtonsoft.Json; using System.IO; using System.Xml; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Utilities; using Newtonsoft.Json.Linq; #if !NET20 using System.Xml.Linq; #endif namespace Newtonsoft.Json.Tests.Converters { [TestFixture] public class XmlNodeConverterTest : TestFixtureBase { private string SerializeXmlNode(XmlNode node) { string json = JsonConvert.SerializeXmlNode(node, Formatting.Indented); XmlNodeReader reader = new XmlNodeReader(node); #if !NET20 XObject xNode; if (node is XmlDocument) { xNode = XDocument.Load(reader); } else if (node is XmlAttribute) { XmlAttribute attribute = (XmlAttribute) node; xNode = new XAttribute(XName.Get(attribute.LocalName, attribute.NamespaceURI), attribute.Value); } else { reader.MoveToContent(); xNode = XNode.ReadFrom(reader); } string linqJson = JsonConvert.SerializeXNode(xNode, Formatting.Indented); Assert.AreEqual(json, linqJson); #endif return json; } private XmlNode DeserializeXmlNode(string json) { return DeserializeXmlNode(json, null); } private XmlNode DeserializeXmlNode(string json, string deserializeRootElementName) { JsonTextReader reader; reader = new JsonTextReader(new StringReader(json)); reader.Read(); XmlNodeConverter converter = new XmlNodeConverter(); if (deserializeRootElementName != null) converter.DeserializeRootElementName = deserializeRootElementName; XmlNode node = (XmlNode)converter.ReadJson(reader, typeof (XmlDocument), null, new JsonSerializer()); #if !NET20 string xmlText = node.OuterXml; reader = new JsonTextReader(new StringReader(json)); reader.Read(); XDocument d = (XDocument) converter.ReadJson(reader, typeof (XDocument), null, new JsonSerializer()); string linqXmlText = d.ToString(SaveOptions.DisableFormatting); if (d.Declaration != null) linqXmlText = d.Declaration + linqXmlText; Assert.AreEqual(xmlText, linqXmlText); #endif return node; } [Test] public void DocumentSerializeIndented() { string xml = @" Web 2.0 Conference October 5 7 Argent Hotel, San Francisco, CA "; XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); string jsonText = SerializeXmlNode(doc); string expected = @"{ ""?xml"": { ""@version"": ""1.0"", ""@standalone"": ""no"" }, ""?xml-stylesheet"": ""href=\""classic.xsl\"" type=\""text/xml\"""", ""span"": { ""@class"": ""vevent"", ""a"": { ""@class"": ""url"", ""@href"": ""http://www.web2con.com/"", ""span"": [ { ""@class"": ""summary"", ""#text"": ""Web 2.0 Conference"", ""#cdata-section"": ""my escaped text"" }, { ""@class"": ""location"", ""#text"": ""Argent Hotel, San Francisco, CA"" } ], ""abbr"": [ { ""@class"": ""dtstart"", ""@title"": ""2005-10-05"", ""#text"": ""October 5"" }, { ""@class"": ""dtend"", ""@title"": ""2005-10-08"", ""#text"": ""7"" } ] } } }"; Assert.AreEqual(expected, jsonText); Console.WriteLine("DocumentSerializeIndented"); Console.WriteLine(jsonText); Console.WriteLine(); } [Test] public void SerializeNodeTypes() { XmlDocument doc = new XmlDocument(); string jsonText; Console.WriteLine("SerializeNodeTypes"); string xml = @" "; XmlDocument document = new XmlDocument(); document.LoadXml(xml); // XmlAttribute XmlAttribute attribute = document.DocumentElement.ChildNodes[0].Attributes["IsDataSet", "urn:schemas-microsoft-com:xml-msdata"]; attribute.Value = "true"; jsonText = JsonConvert.SerializeXmlNode(attribute); Console.WriteLine(jsonText); Assert.AreEqual(@"{""@msdata:IsDataSet"":""true""}", jsonText); #if !NET20 XDocument d = XDocument.Parse(xml); XAttribute a = d.Root.Element("{http://www.w3.org/2001/XMLSchema}element").Attribute("{urn:schemas-microsoft-com:xml-msdata}IsDataSet"); jsonText = JsonConvert.SerializeXNode(a); Assert.AreEqual(@"{""@msdata:IsDataSet"":""true""}", jsonText); #endif // XmlProcessingInstruction XmlProcessingInstruction instruction = doc.CreateProcessingInstruction("xml-stylesheet", @"href=""classic.xsl"" type=""text/xml"""); jsonText = JsonConvert.SerializeXmlNode(instruction); Console.WriteLine(jsonText); Assert.AreEqual(@"{""?xml-stylesheet"":""href=\""classic.xsl\"" type=\""text/xml\""""}", jsonText); // XmlProcessingInstruction XmlCDataSection cDataSection = doc.CreateCDataSection("true"); jsonText = JsonConvert.SerializeXmlNode(cDataSection); Console.WriteLine(jsonText); Assert.AreEqual(@"{""#cdata-section"":""true""}", jsonText); // XmlElement XmlElement element = doc.CreateElement("xs", "Choice", "http://www.w3.org/2001/XMLSchema"); element.SetAttributeNode(doc.CreateAttribute("msdata", "IsDataSet", "urn:schemas-microsoft-com:xml-msdata")); XmlAttribute aa = doc.CreateAttribute(@"xmlns", "xs", "http://www.w3.org/2000/xmlns/"); aa.Value = "http://www.w3.org/2001/XMLSchema"; element.SetAttributeNode(aa); aa = doc.CreateAttribute(@"xmlns", "msdata", "http://www.w3.org/2000/xmlns/"); aa.Value = "urn:schemas-microsoft-com:xml-msdata"; element.SetAttributeNode(aa); element.AppendChild(instruction); element.AppendChild(cDataSection); doc.AppendChild(element); jsonText = JsonConvert.SerializeXmlNode(element, Formatting.Indented); Assert.AreEqual(@"{ ""xs:Choice"": { ""@msdata:IsDataSet"": """", ""@xmlns:xs"": ""http://www.w3.org/2001/XMLSchema"", ""@xmlns:msdata"": ""urn:schemas-microsoft-com:xml-msdata"", ""?xml-stylesheet"": ""href=\""classic.xsl\"" type=\""text/xml\"""", ""#cdata-section"": ""true"" } }", jsonText); } [Test] public void DocumentFragmentSerialize() { XmlDocument doc = new XmlDocument(); XmlDocumentFragment fragement = doc.CreateDocumentFragment(); fragement.InnerXml = "widgetwidget"; string jsonText = JsonConvert.SerializeXmlNode(fragement); string expected = @"{""Item"":[""widget"",""widget""]}"; Assert.AreEqual(expected, jsonText); Console.WriteLine("DocumentFragmentSerialize"); Console.WriteLine(jsonText); Console.WriteLine(); } [Test] public void NamespaceSerializeDeserialize() { string xml = @" "; XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); string jsonText = SerializeXmlNode(doc); string expected = @"{ ""?xml"": { ""@version"": ""1.0"", ""@encoding"": ""utf-8"" }, ""xs:schema"": { ""@xs:id"": ""SomeID"", ""@xmlns"": """", ""@xmlns:xs"": ""http://www.w3.org/2001/XMLSchema"", ""@xmlns:msdata"": ""urn:schemas-microsoft-com:xml-msdata"", ""xs:element"": { ""@name"": ""MyDataSet"", ""@msdata:IsDataSet"": ""true"", ""xs:complexType"": { ""xs:choice"": { ""@maxOccurs"": ""unbounded"", ""xs:element"": { ""@name"": ""customers"", ""xs:complexType"": { ""xs:sequence"": { ""xs:element"": [ { ""@name"": ""CustomerID"", ""@type"": ""xs:integer"", ""@minOccurs"": ""0"" }, { ""@name"": ""CompanyName"", ""@type"": ""xs:string"", ""@minOccurs"": ""0"" }, { ""@name"": ""Phone"", ""@type"": ""xs:string"" } ] } } } } } } } }"; Assert.AreEqual(expected, jsonText); XmlDocument deserializedDoc = (XmlDocument)DeserializeXmlNode(jsonText); Assert.AreEqual(doc.InnerXml, deserializedDoc.InnerXml); Console.WriteLine("NamespaceSerializeDeserialize"); Console.WriteLine(jsonText); Console.WriteLine(deserializedDoc.InnerXml); Console.WriteLine(); } [Test] public void DocumentDeserialize() { string jsonText = @"{ ""?xml"": { ""@version"": ""1.0"", ""@standalone"": ""no"" }, ""span"": { ""@class"": ""vevent"", ""a"": { ""@class"": ""url"", ""@href"": ""http://www.web2con.com/"", ""span"": { ""@class"": ""summary"", ""#text"": ""Web 2.0 Conference"", ""#cdata-section"": ""my escaped text"" } } } }"; XmlDocument doc = (XmlDocument)DeserializeXmlNode(jsonText); string expected = @" Web 2.0 Conference "; string formattedXml = GetIndentedInnerXml(doc); Console.WriteLine("DocumentDeserialize"); Console.WriteLine(formattedXml); Console.WriteLine(); Assert.AreEqual(expected, formattedXml); } private string GetIndentedInnerXml(XmlNode node) { XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; StringWriter sw = new StringWriter(); using (XmlWriter writer = XmlWriter.Create(sw, settings)) { node.WriteTo(writer); } return sw.ToString(); } [Test] public void SingleTextNode() { string xml = @" Alan http://www.google.com Louis http://www.yahoo.com "; XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); string jsonText = SerializeXmlNode(doc); XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText); Assert.AreEqual(doc.InnerXml, newDoc.InnerXml); } [Test] public void EmptyNode() { string xml = @" Alan Louis http://www.yahoo.com "; XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); string jsonText = SerializeXmlNode(doc); Console.WriteLine(jsonText); XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText); Assert.AreEqual(doc.InnerXml, newDoc.InnerXml); } [Test] public void OtherElementDataTypes() { string jsonText = @"{""?xml"":{""@version"":""1.0"",""@standalone"":""no""},""root"":{""person"":[{""@id"":""1"",""Float"":2.5,""Integer"":99},{""@id"":""2"",""Boolean"":true,""date"":""\/Date(954374400000)\/""}]}}"; XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText); string expected = @"2.599true2000-03-30T00:00:00Z"; Assert.AreEqual(expected, newDoc.InnerXml); } [Test] [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "XmlNodeConverter can only convert JSON that begins with an object.")] public void NoRootObject() { XmlDocument newDoc = (XmlDocument)JsonConvert.DeserializeXmlNode(@"[1]"); } [Test] [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "JSON root object has multiple properties. The root object must have a single property in order to create a valid XML document. Consider specifing a DeserializeRootElementName.")] public void RootObjectMultipleProperties() { XmlDocument newDoc = (XmlDocument)JsonConvert.DeserializeXmlNode(@"{Prop1:1,Prop2:2}"); } [Test] public void JavaScriptConstructor() { string jsonText = @"{root:{r:new Date(34343, 55)}}"; XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText); string expected = @"3434355"; Assert.AreEqual(expected, newDoc.InnerXml); string json = SerializeXmlNode(newDoc); expected = @"{ ""root"": { ""r"": { ""Date"": [ ""34343"", ""55"" ] } } }"; Assert.AreEqual(expected, json); } [Test] public void ForceJsonArray() { string arrayXml = @" Alan http://www.google.com Admin "; XmlDocument arrayDoc = new XmlDocument(); arrayDoc.LoadXml(arrayXml); string arrayJsonText = SerializeXmlNode(arrayDoc); string expected = @"{ ""root"": { ""person"": { ""@id"": ""1"", ""name"": ""Alan"", ""url"": ""http://www.google.com"", ""role"": [ ""Admin"" ] } } }"; Assert.AreEqual(expected, arrayJsonText); arrayXml = @" Alan http://www.google.com Admin1 Admin2 "; arrayDoc = new XmlDocument(); arrayDoc.LoadXml(arrayXml); arrayJsonText = SerializeXmlNode(arrayDoc); expected = @"{ ""root"": { ""person"": { ""@id"": ""1"", ""name"": ""Alan"", ""url"": ""http://www.google.com"", ""role"": [ ""Admin1"", ""Admin2"" ] } } }"; Assert.AreEqual(expected, arrayJsonText); arrayXml = @" Alan http://www.google.com Admin1 "; arrayDoc = new XmlDocument(); arrayDoc.LoadXml(arrayXml); arrayJsonText = SerializeXmlNode(arrayDoc); expected = @"{ ""root"": { ""person"": { ""@id"": ""1"", ""name"": ""Alan"", ""url"": ""http://www.google.com"", ""role"": ""Admin1"" } } }"; Assert.AreEqual(expected, arrayJsonText); } [Test] [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "JSON root object has multiple properties. The root object must have a single property in order to create a valid XML document. Consider specifing a DeserializeRootElementName.")] public void MultipleRootPropertiesXmlDocument() { string json = @"{""count"": 773840,""photos"": null}"; JsonConvert.DeserializeXmlNode(json); } #if !NET20 [Test] [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "JSON root object has multiple properties. The root object must have a single property in order to create a valid XML document. Consider specifing a DeserializeRootElementName.")] public void MultipleRootPropertiesXDocument() { string json = @"{""count"": 773840,""photos"": null}"; JsonConvert.DeserializeXNode(json); } #endif [Test] public void MultipleRootPropertiesAddRootElement() { string json = @"{""count"": 773840,""photos"": 773840}"; XmlDocument newDoc = JsonConvert.DeserializeXmlNode(json, "myRoot"); Assert.AreEqual(@"773840773840", newDoc.InnerXml); #if !NET20 XDocument newXDoc = JsonConvert.DeserializeXNode(json, "myRoot"); Assert.AreEqual(@"773840773840", newXDoc.ToString(SaveOptions.DisableFormatting)); #endif } [Test] public void NestedArrays() { string json = @"{ ""available_sizes"": [ [ ""assets/images/resized/0001/1070/11070v1-max-150x150.jpg"", ""assets/images/resized/0001/1070/11070v1-max-150x150.jpg"" ], [ ""assets/images/resized/0001/1070/11070v1-max-250x250.jpg"", ""assets/images/resized/0001/1070/11070v1-max-250x250.jpg"" ], [ ""assets/images/resized/0001/1070/11070v1-max-250x250.jpg"" ] ] }"; XmlDocument newDoc = JsonConvert.DeserializeXmlNode(json, "myRoot"); string xml = IndentXml(newDoc.InnerXml); Assert.AreEqual(@" assets/images/resized/0001/1070/11070v1-max-150x150.jpg assets/images/resized/0001/1070/11070v1-max-150x150.jpg assets/images/resized/0001/1070/11070v1-max-250x250.jpg assets/images/resized/0001/1070/11070v1-max-250x250.jpg assets/images/resized/0001/1070/11070v1-max-250x250.jpg ", IndentXml(newDoc.InnerXml)); #if !NET20 XDocument newXDoc = JsonConvert.DeserializeXNode(json, "myRoot"); Assert.AreEqual(@" assets/images/resized/0001/1070/11070v1-max-150x150.jpg assets/images/resized/0001/1070/11070v1-max-150x150.jpg assets/images/resized/0001/1070/11070v1-max-250x250.jpg assets/images/resized/0001/1070/11070v1-max-250x250.jpg assets/images/resized/0001/1070/11070v1-max-250x250.jpg ", IndentXml(newXDoc.ToString(SaveOptions.DisableFormatting))); #endif string newJson = JsonConvert.SerializeXmlNode(newDoc, Formatting.Indented); Console.WriteLine(newJson); } [Test] public void RoundTripNestedArrays() { string json = @"{ ""available_sizes"": [ [ ""assets/images/resized/0001/1070/11070v1-max-150x150.jpg"", ""assets/images/resized/0001/1070/11070v1-max-150x150.jpg"" ], [ ""assets/images/resized/0001/1070/11070v1-max-250x250.jpg"", ""assets/images/resized/0001/1070/11070v1-max-250x250.jpg"" ], [ ""assets/images/resized/0001/1070/11070v1-max-250x250.jpg"" ] ] }"; XmlDocument newDoc = JsonConvert.DeserializeXmlNode(json, "myRoot", true); Assert.AreEqual(@" assets/images/resized/0001/1070/11070v1-max-150x150.jpg assets/images/resized/0001/1070/11070v1-max-150x150.jpg assets/images/resized/0001/1070/11070v1-max-250x250.jpg assets/images/resized/0001/1070/11070v1-max-250x250.jpg assets/images/resized/0001/1070/11070v1-max-250x250.jpg ", IndentXml(newDoc.InnerXml)); #if !NET20 XDocument newXDoc = JsonConvert.DeserializeXNode(json, "myRoot", true); Console.WriteLine(IndentXml(newXDoc.ToString(SaveOptions.DisableFormatting))); Assert.AreEqual(@" assets/images/resized/0001/1070/11070v1-max-150x150.jpg assets/images/resized/0001/1070/11070v1-max-150x150.jpg assets/images/resized/0001/1070/11070v1-max-250x250.jpg assets/images/resized/0001/1070/11070v1-max-250x250.jpg assets/images/resized/0001/1070/11070v1-max-250x250.jpg ", IndentXml(newXDoc.ToString(SaveOptions.DisableFormatting))); #endif string newJson = JsonConvert.SerializeXmlNode(newDoc, Formatting.Indented, true); Assert.AreEqual(json, newJson); } [Test] public void MultipleNestedArraysToXml() { string json = @"{ ""available_sizes"": [ [ [113, 150], ""assets/images/resized/0001/1070/11070v1-max-150x150.jpg"" ], [ [189, 250], ""assets/images/resized/0001/1070/11070v1-max-250x250.jpg"" ], [ [341, 450], ""assets/images/resized/0001/1070/11070v1-max-450x450.jpg"" ] ] }"; XmlDocument newDoc = JsonConvert.DeserializeXmlNode(json, "myRoot"); Assert.AreEqual(@"113150assets/images/resized/0001/1070/11070v1-max-150x150.jpg189250assets/images/resized/0001/1070/11070v1-max-250x250.jpg341450assets/images/resized/0001/1070/11070v1-max-450x450.jpg", newDoc.InnerXml); #if !NET20 XDocument newXDoc = JsonConvert.DeserializeXNode(json, "myRoot"); Assert.AreEqual(@"113150assets/images/resized/0001/1070/11070v1-max-150x150.jpg189250assets/images/resized/0001/1070/11070v1-max-250x250.jpg341450assets/images/resized/0001/1070/11070v1-max-450x450.jpg", newXDoc.ToString(SaveOptions.DisableFormatting)); #endif } [Test] public void Encoding() { XmlDocument doc = new XmlDocument(); doc.LoadXml(@"O""Connor"); // i use "" so it will be easier to see the problem string json = SerializeXmlNode(doc); Assert.AreEqual(@"{ ""name"": ""O\""Connor"" }", json); } [Test] public void SerializeComment() { string xml = @" Text "; XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); string jsonText = SerializeXmlNode(doc); string expected = @"{ ""span"": { ""@class"": ""vevent"", ""a"": { ""@class"": ""url"", ""@href"": ""http://www.web2con.com/"", ""#text"": ""Text"" }/* Hi! */ } }"; Assert.AreEqual(expected, jsonText); XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText); Assert.AreEqual(@"Text", newDoc.InnerXml); } [Test] public void SerializeExample() { string xml = @" Alan http://www.google.com Louis http://www.yahoo.com "; XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); string jsonText = SerializeXmlNode(doc); // { // "?xml": { // "@version": "1.0", // "@standalone": "no" // }, // "root": { // "person": [ // { // "@id": "1", // "name": "Alan", // "url": "http://www.google.com" // }, // { // "@id": "2", // "name": "Louis", // "url": "http://www.yahoo.com" // } // ] // } // } // format jsonText = JObject.Parse(jsonText).ToString(); Assert.AreEqual(@"{ ""?xml"": { ""@version"": ""1.0"", ""@standalone"": ""no"" }, ""root"": { ""person"": [ { ""@id"": ""1"", ""name"": ""Alan"", ""url"": ""http://www.google.com"" }, { ""@id"": ""2"", ""name"": ""Louis"", ""url"": ""http://www.yahoo.com"" } ] } }", jsonText); XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText); Assert.AreEqual(doc.InnerXml, newDoc.InnerXml); } [Test] public void DeserializeExample() { string json = @"{ ""?xml"": { ""@version"": ""1.0"", ""@standalone"": ""no"" }, ""root"": { ""person"": [ { ""@id"": ""1"", ""name"": ""Alan"", ""url"": ""http://www.google.com"" }, { ""@id"": ""2"", ""name"": ""Louis"", ""url"": ""http://www.yahoo.com"" } ] } }"; XmlDocument doc = (XmlDocument)DeserializeXmlNode(json); // // // // Alan // http://www.google.com // // // Louis // http://www.yahoo.com // // Assert.AreEqual(@" Alan http://www.google.com Louis http://www.yahoo.com ".Replace(Environment.NewLine, string.Empty), doc.InnerXml); } [Test] public void SerializeDeserializeSpecialProperties() { PreserveReferencesHandlingTests.CircularDictionary circularDictionary = new PreserveReferencesHandlingTests.CircularDictionary(); circularDictionary.Add("other", new PreserveReferencesHandlingTests.CircularDictionary { { "blah", null } }); circularDictionary.Add("self", circularDictionary); string json = JsonConvert.SerializeObject(circularDictionary, Formatting.Indented, new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All }); Assert.AreEqual(@"{ ""$id"": ""1"", ""other"": { ""$id"": ""2"", ""blah"": null }, ""self"": { ""$ref"": ""1"" } }", json); XmlNode node = DeserializeXmlNode(json, "root"); string xml = GetIndentedInnerXml(node); string expected = @" "; Assert.AreEqual(expected, xml); string xmlJson = SerializeXmlNode(node); string expectedXmlJson = @"{ ""root"": { ""$id"": ""1"", ""other"": { ""$id"": ""2"", ""blah"": null }, ""self"": { ""$ref"": ""1"" } } }"; Assert.AreEqual(expectedXmlJson, xmlJson); } [Test] [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "XmlNodeConverter cannot convert JSON with an empty property name to XML.")] public void EmptyPropertyName() { string json = @"{ ""8452309520V2"": { """": { ""CLIENT"": { ""ID_EXPIRATION_1"": { ""VALUE"": ""12/12/2000"", ""DATATYPE"": ""D"", ""MSG"": ""Missing Identification Exp. Date 1"" }, ""ID_ISSUEDATE_1"": { ""VALUE"": """", ""DATATYPE"": ""D"", ""MSG"": ""Missing Identification Issue Date 1"" } } }, ""457463534534"": { ""ACCOUNT"": { ""FUNDING_SOURCE"": { ""VALUE"": ""FS0"", ""DATATYPE"": ""L"", ""MSG"": ""Missing Source of Funds"" } } } } }{ ""34534634535345"": { """": { ""CLIENT"": { ""ID_NUMBER_1"": { ""VALUE"": """", ""DATATYPE"": ""S"", ""MSG"": ""Missing Picture ID"" }, ""ID_EXPIRATION_1"": { ""VALUE"": ""12/12/2000"", ""DATATYPE"": ""D"", ""MSG"": ""Missing Picture ID"" }, ""WALK_IN"": { ""VALUE"": """", ""DATATYPE"": ""L"", ""MSG"": ""Missing Walk in"" }, ""PERSONAL_MEETING"": { ""VALUE"": ""PM1"", ""DATATYPE"": ""L"", ""MSG"": ""Missing Met Client in Person"" }, ""ID_ISSUEDATE_1"": { ""VALUE"": """", ""DATATYPE"": ""D"", ""MSG"": ""Missing Picture ID"" }, ""PHOTO_ID"": { ""VALUE"": """", ""DATATYPE"": ""L"", ""MSG"": ""Missing Picture ID"" }, ""ID_TYPE_1"": { ""VALUE"": """", ""DATATYPE"": ""L"", ""MSG"": ""Missing Picture ID"" } } }, ""45635624523"": { ""ACCOUNT"": { ""FUNDING_SOURCE"": { ""VALUE"": ""FS1"", ""DATATYPE"": ""L"", ""MSG"": ""Missing Source of Funds"" } } } } }"; DeserializeXmlNode(json); } [Test] public void SingleItemArrayPropertySerialization() { Product product = new Product(); product.Name = "Apple"; product.ExpiryDate = new DateTime(2008, 12, 28, 0, 0, 0, DateTimeKind.Utc); product.Price = 3.99M; product.Sizes = new string[] { "Small" }; string output = JsonConvert.SerializeObject(product, new IsoDateTimeConverter()); XmlDocument xmlProduct = JsonConvert.DeserializeXmlNode(output, "product", true); Assert.AreEqual(@" Apple 2008-12-28T00:00:00Z 3.99 Small ", IndentXml(xmlProduct.InnerXml)); string output2 = JsonConvert.SerializeXmlNode(xmlProduct.DocumentElement, Formatting.Indented); Assert.AreEqual(@"{ ""product"": { ""Name"": ""Apple"", ""ExpiryDate"": ""2008-12-28T00:00:00Z"", ""Price"": ""3.99"", ""Sizes"": [ ""Small"" ] } }", output2); } public class TestComplexArrayClass { public string Name { get; set; } public IList Products { get; set; } } [Test] public void ComplexSingleItemArrayPropertySerialization() { TestComplexArrayClass o = new TestComplexArrayClass { Name = "Hi", Products = new List { new Product { Name = "First" } } }; string output = JsonConvert.SerializeObject(o, new IsoDateTimeConverter()); XmlDocument xmlProduct = JsonConvert.DeserializeXmlNode(output, "test", true); Assert.AreEqual(@" Hi First 2000-01-01T00:00:00Z 0 ", IndentXml(xmlProduct.InnerXml)); string output2 = JsonConvert.SerializeXmlNode(xmlProduct.DocumentElement, Formatting.Indented, true); Assert.AreEqual(@"{ ""Name"": ""Hi"", ""Products"": [ { ""Name"": ""First"", ""ExpiryDate"": ""2000-01-01T00:00:00Z"", ""Price"": ""0"", ""Sizes"": null } ] }", output2); } private string IndentXml(string xml) { XmlReader reader = XmlReader.Create(new StringReader(xml)); StringWriter sw = new StringWriter(); XmlWriter writer = XmlWriter.Create(sw, new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }); while (reader.Read()) { writer.WriteNode(reader, false); } writer.Flush(); return sw.ToString(); } [Test] public void OmitRootObject() { string xml = @" Hi Hi First 2000-01-01T00:00:00Z 0 "; XmlDocument d = new XmlDocument(); d.LoadXml(xml); string output = JsonConvert.SerializeXmlNode(d, Formatting.Indented, true); Assert.AreEqual(@"{ ""Name"": [ ""Hi"", ""Hi"" ], ""Products"": [ { ""Name"": ""First"", ""ExpiryDate"": ""2000-01-01T00:00:00Z"", ""Price"": ""0"", ""Sizes"": null } ] }", output); } [Test] public void EmtpyElementWithArrayAttributeShouldWriteAttributes() { string xml = @" "; XmlDocument d = new XmlDocument(); d.LoadXml(xml); string json = JsonConvert.SerializeXmlNode(d, Formatting.Indented); Assert.AreEqual(@"{ ""?xml"": { ""@version"": ""1.0"", ""@encoding"": ""utf-8"" }, ""root"": { ""A"": { ""B"": [ { ""@name"": ""sample"" } ], ""C"": [ null, null ] } } }", json); } [Test] public void DeserializeNonInt64IntegerValues() { var dict = new Dictionary { { "Int16", (short)1 }, { "Float", 2f }, { "Int32", 3 } }; var obj = JObject.FromObject(dict); var serializer = JsonSerializer.Create(new JsonSerializerSettings { Converters = { new XmlNodeConverter() { DeserializeRootElementName = "root" } } }); using (var reader = obj.CreateReader()) { var value = (XmlDocument)serializer.Deserialize(reader, typeof(XmlDocument)); Assert.AreEqual(@"123", value.InnerXml); } } [Test] public void DeserializingBooleanValues() { MemoryStream ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(@"{root:{""@booleanType"":true}}")); MemoryStream xml = new MemoryStream(); JsonBodyToSoapXml(ms, xml); string xmlString = System.Text.Encoding.UTF8.GetString(xml.ToArray()); Assert.AreEqual(@"", xmlString); } private static void JsonBodyToSoapXml(Stream json, Stream xml) { Newtonsoft.Json.JsonSerializerSettings settings = new Newtonsoft.Json.JsonSerializerSettings(); settings.Converters.Add(new Newtonsoft.Json.Converters.XmlNodeConverter()); Newtonsoft.Json.JsonSerializer serializer = Newtonsoft.Json.JsonSerializer.Create(settings); using (Newtonsoft.Json.JsonTextReader reader = new Newtonsoft.Json.JsonTextReader(new System.IO.StreamReader(json))) { XmlDocument doc = (XmlDocument)serializer.Deserialize(reader, typeof(XmlDocument)); if (reader.Read() && reader.TokenType != JsonToken.Comment) throw new JsonSerializationException("Additional text found in JSON string after finishing deserializing object."); using (XmlWriter writer = XmlWriter.Create(xml)) { doc.Save(writer); } } } #if !NET20 [Test] public void DeserializeXNodeDefaultNamespace() { string xaml = @" "; string json = JsonConvert.SerializeXNode(XDocument.Parse(xaml), Formatting.Indented); string expectedJson = @"{ ""Grid"": { ""@xmlns"": ""http://schemas.microsoft.com/winfx/2006/xaml/presentation"", ""@xmlns:x"": ""http://schemas.microsoft.com/winfx/2006/xaml"", ""@xmlns:toolkit"": ""clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"", ""@Style"": ""{StaticResource trimFormGrid}"", ""@x:Name"": ""TrimObjectForm"", ""Grid.ColumnDefinitions"": { ""ColumnDefinition"": [ { ""@Width"": ""63*"" }, { ""@Width"": ""320*"" } ] }, ""Grid.RowDefinitions"": { ""@xmlns"": """", ""RowDefinition"": [ null, null, null, null, null, null, null, null ] }, ""TextBox"": [ { ""@Style"": ""{StaticResource trimFormGrid_TB}"", ""@Text"": ""{Binding TypedTitle, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordTypedTitle"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""0"", ""@xmlns"": """" }, { ""@Style"": ""{StaticResource trimFormGrid_TB}"", ""@Text"": ""{Binding ExternalReference, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordExternalReference"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""1"", ""@xmlns"": """" }, { ""@Style"": ""{StaticResource trimFormGrid_TB}"", ""@Text"": ""{Binding Author, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordAuthor"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""4"", ""@xmlns"": """" }, { ""@Style"": ""{StaticResource trimFormGrid_TB}"", ""@Text"": ""{Binding Container, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordContainer"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""5"", ""@xmlns"": """" }, { ""@Style"": ""{StaticResource trimFormGrid_TB}"", ""@Text"": ""{Binding IsEnclosed, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordIsEnclosed"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""6"", ""@xmlns"": """" }, { ""@Style"": ""{StaticResource trimFormGrid_TB}"", ""@Text"": ""{Binding Assignee, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordAssignee"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""7"", ""@xmlns"": """" } ], ""toolkit:DatePicker"": [ { ""@Style"": ""{StaticResource trimFormGrid_DP}"", ""@Value"": ""{Binding DateCreated, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordDateCreated"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""2"" }, { ""@Style"": ""{StaticResource trimFormGrid_DP}"", ""@Value"": ""{Binding DateDue, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordDateDue"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""3"" } ], ""TextBlock"": [ { ""@Grid.Column"": ""0"", ""@Text"": ""Title (Free Text Part)"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""0"", ""@xmlns"": """" }, { ""@Grid.Column"": ""0"", ""@Text"": ""External ID"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""1"", ""@xmlns"": """" }, { ""@Grid.Column"": ""0"", ""@Text"": ""Date Created"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""2"", ""@xmlns"": """" }, { ""@Grid.Column"": ""0"", ""@Text"": ""Date Due"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""3"", ""@xmlns"": """" }, { ""@Grid.Column"": ""0"", ""@Text"": ""Author"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""4"", ""@xmlns"": """" }, { ""@Grid.Column"": ""0"", ""@Text"": ""Container"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""5"", ""@xmlns"": """" }, { ""@Grid.Column"": ""0"", ""@Text"": ""Enclosed?"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""6"", ""@xmlns"": """" }, { ""@Grid.Column"": ""0"", ""@Text"": ""Assignee"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""7"", ""@xmlns"": """" } ] } }"; Assert.AreEqual(expectedJson, json); XNode node = JsonConvert.DeserializeXNode(json); string xaml2 = node.ToString(); string expectedXaml = @" "; Assert.AreEqual(expectedXaml, xaml2); } #endif [Test] public void DeserializeXmlNodeDefaultNamespace() { string xaml = @" "; XmlDocument document = new XmlDocument(); document.LoadXml(xaml); string json = JsonConvert.SerializeXmlNode(document, Formatting.Indented); string expectedJson = @"{ ""Grid"": { ""@xmlns"": ""http://schemas.microsoft.com/winfx/2006/xaml/presentation"", ""@xmlns:x"": ""http://schemas.microsoft.com/winfx/2006/xaml"", ""@xmlns:toolkit"": ""clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"", ""@Style"": ""{StaticResource trimFormGrid}"", ""@x:Name"": ""TrimObjectForm"", ""Grid.ColumnDefinitions"": { ""ColumnDefinition"": [ { ""@Width"": ""63*"" }, { ""@Width"": ""320*"" } ] }, ""Grid.RowDefinitions"": { ""@xmlns"": """", ""RowDefinition"": [ null, null, null, null, null, null, null, null ] }, ""TextBox"": [ { ""@Style"": ""{StaticResource trimFormGrid_TB}"", ""@Text"": ""{Binding TypedTitle, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordTypedTitle"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""0"", ""@xmlns"": """" }, { ""@Style"": ""{StaticResource trimFormGrid_TB}"", ""@Text"": ""{Binding ExternalReference, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordExternalReference"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""1"", ""@xmlns"": """" }, { ""@Style"": ""{StaticResource trimFormGrid_TB}"", ""@Text"": ""{Binding Author, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordAuthor"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""4"", ""@xmlns"": """" }, { ""@Style"": ""{StaticResource trimFormGrid_TB}"", ""@Text"": ""{Binding Container, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordContainer"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""5"", ""@xmlns"": """" }, { ""@Style"": ""{StaticResource trimFormGrid_TB}"", ""@Text"": ""{Binding IsEnclosed, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordIsEnclosed"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""6"", ""@xmlns"": """" }, { ""@Style"": ""{StaticResource trimFormGrid_TB}"", ""@Text"": ""{Binding Assignee, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordAssignee"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""7"", ""@xmlns"": """" } ], ""toolkit:DatePicker"": [ { ""@Style"": ""{StaticResource trimFormGrid_DP}"", ""@Value"": ""{Binding DateCreated, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordDateCreated"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""2"" }, { ""@Style"": ""{StaticResource trimFormGrid_DP}"", ""@Value"": ""{Binding DateDue, Converter={StaticResource trimPropertyConverter}}"", ""@Name"": ""RecordDateDue"", ""@Grid.Column"": ""1"", ""@Grid.Row"": ""3"" } ], ""TextBlock"": [ { ""@Grid.Column"": ""0"", ""@Text"": ""Title (Free Text Part)"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""0"", ""@xmlns"": """" }, { ""@Grid.Column"": ""0"", ""@Text"": ""External ID"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""1"", ""@xmlns"": """" }, { ""@Grid.Column"": ""0"", ""@Text"": ""Date Created"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""2"", ""@xmlns"": """" }, { ""@Grid.Column"": ""0"", ""@Text"": ""Date Due"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""3"", ""@xmlns"": """" }, { ""@Grid.Column"": ""0"", ""@Text"": ""Author"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""4"", ""@xmlns"": """" }, { ""@Grid.Column"": ""0"", ""@Text"": ""Container"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""5"", ""@xmlns"": """" }, { ""@Grid.Column"": ""0"", ""@Text"": ""Enclosed?"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""6"", ""@xmlns"": """" }, { ""@Grid.Column"": ""0"", ""@Text"": ""Assignee"", ""@Style"": ""{StaticResource trimFormGrid_LBL}"", ""@Grid.Row"": ""7"", ""@xmlns"": """" } ] } }"; Assert.AreEqual(expectedJson, json); XmlNode node = JsonConvert.DeserializeXmlNode(json); StringWriter sw = new StringWriter(); XmlWriter writer = XmlWriter.Create(sw, new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }); node.WriteTo(writer); writer.Flush(); string xaml2 = sw.ToString(); string expectedXaml = @" "; Assert.AreEqual(expectedXaml, xaml2); } } } #endif