diff --git a/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs b/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs
index a446d61d..24a93032 100644
--- a/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs
+++ b/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs
@@ -32,11 +32,71 @@ 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
{
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)
+ {
+ JsonTextReader reader;
+
+ reader = new JsonTextReader(new StringReader(json));
+ reader.Read();
+ XmlNodeConverter converter = new XmlNodeConverter();
+
+ XmlNode node = (XmlNode)converter.ReadJson(reader, typeof (XmlDocument), new JsonSerializer());
+
+#if !NET20
+ string xmlText = node.OuterXml;
+
+ reader = new JsonTextReader(new StringReader(json));
+ reader.Read();
+ XDocument d = (XDocument) converter.ReadJson(reader, typeof (XDocument), 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()
{
@@ -53,19 +113,7 @@ namespace Newtonsoft.Json.Tests.Converters
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
- StringWriter sw = new StringWriter();
-
- using (JsonWriter jsonWriter = new JsonTextWriter(sw))
- {
- jsonWriter.Formatting = Formatting.Indented;
-
- JsonSerializer jsonSerializer = new JsonSerializer();
- jsonSerializer.Converters.Add(new XmlNodeConverter());
-
- jsonSerializer.Serialize(jsonWriter, doc);
- }
-
- string jsonText = sw.ToString();
+ string jsonText = SerializeXmlNode(doc);
string expected = @"{
""?xml"": {
""@version"": ""1.0"",
@@ -119,8 +167,20 @@ namespace Newtonsoft.Json.Tests.Converters
Console.WriteLine("SerializeNodeTypes");
+ string xml = @"
+
+
+
+";
+
+ XmlDocument document = new XmlDocument();
+ document.LoadXml(xml);
+
// XmlAttribute
- XmlAttribute attribute = doc.CreateAttribute("msdata:IsDataSet");
+ XmlAttribute attribute = document.DocumentElement.ChildNodes[0].Attributes["IsDataSet", "urn:schemas-microsoft-com:xml-msdata"];
attribute.Value = "true";
jsonText = JsonConvert.SerializeXmlNode(attribute);
@@ -128,6 +188,14 @@ namespace Newtonsoft.Json.Tests.Converters
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""");
@@ -148,16 +216,33 @@ namespace Newtonsoft.Json.Tests.Converters
// XmlElement
- XmlElement element = doc.CreateElement("xs:Choice");
- element.SetAttributeNode(attribute);
+ 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);
- jsonText = JsonConvert.SerializeXmlNode(element);
+ doc.AppendChild(element);
- Console.WriteLine(jsonText);
- Assert.AreEqual(@"{""xs:Choice"":{""@msdata:IsDataSet"":""true"",""?xml-stylesheet"":""href=\""classic.xsl\"" type=\""text/xml\"""",""#cdata-section"":""true""}}", jsonText);
+ 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]
@@ -210,9 +295,56 @@ namespace Newtonsoft.Json.Tests.Converters
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
- string jsonText = JsonConvert.SerializeXmlNode(doc);
+ string jsonText = SerializeXmlNode(doc);
- XmlDocument deserializedDoc = (XmlDocument)JsonConvert.DeserializeXmlNode(jsonText);
+ 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);
@@ -244,7 +376,7 @@ namespace Newtonsoft.Json.Tests.Converters
}
}";
- XmlDocument doc = (XmlDocument)JsonConvert.DeserializeXmlNode(jsonText);
+ XmlDocument doc = (XmlDocument)DeserializeXmlNode(jsonText);
string expected = @"
@@ -295,9 +427,9 @@ namespace Newtonsoft.Json.Tests.Converters
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
- string jsonText = JsonConvert.SerializeXmlNode(doc);
+ string jsonText = SerializeXmlNode(doc);
- XmlDocument newDoc = (XmlDocument)JsonConvert.DeserializeXmlNode(jsonText);
+ XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText);
Assert.AreEqual(doc.InnerXml, newDoc.InnerXml);
}
@@ -320,11 +452,11 @@ namespace Newtonsoft.Json.Tests.Converters
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
- string jsonText = JsonConvert.SerializeXmlNode(doc);
+ string jsonText = SerializeXmlNode(doc);
Console.WriteLine(jsonText);
- XmlDocument newDoc = (XmlDocument)JsonConvert.DeserializeXmlNode(jsonText);
+ XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText);
Assert.AreEqual(doc.InnerXml, newDoc.InnerXml);
}
@@ -334,7 +466,7 @@ namespace Newtonsoft.Json.Tests.Converters
{
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)JsonConvert.DeserializeXmlNode(jsonText);
+ XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText);
string expected = @"2.599true2000-03-30T00:00:00Z";
@@ -360,14 +492,25 @@ namespace Newtonsoft.Json.Tests.Converters
{
string jsonText = @"{root:{r:new Date(34343, 55)}}";
- XmlDocument newDoc = (XmlDocument)JsonConvert.DeserializeXmlNode(jsonText);
+ XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText);
- string expected = @"<-Date>34343-Date><-Date>55-Date>";
+ string expected = @"3434355";
Assert.AreEqual(expected, newDoc.InnerXml);
- string json = JsonConvert.SerializeXmlNode(newDoc);
- Assert.AreEqual(@"{""root"":{""r"":new Date(""34343"",""55"")}}", json);
+ string json = SerializeXmlNode(newDoc);
+ expected = @"{
+ ""root"": {
+ ""r"": {
+ ""Date"": [
+ ""34343"",
+ ""55""
+ ]
+ }
+ }
+}";
+
+ Assert.AreEqual(expected, json);
}
[Test]
@@ -384,8 +527,20 @@ namespace Newtonsoft.Json.Tests.Converters
XmlDocument arrayDoc = new XmlDocument();
arrayDoc.LoadXml(arrayXml);
- string arrayJsonText = JsonConvert.SerializeXmlNode(arrayDoc);
- Assert.AreEqual(@"{""root"":{""person"":{""@id"":""1"",""name"":""Alan"",""url"":""http://www.google.com"",""role"":[""Admin""]}}}", arrayJsonText);
+ string arrayJsonText = SerializeXmlNode(arrayDoc);
+ string expected = @"{
+ ""root"": {
+ ""person"": {
+ ""@id"": ""1"",
+ ""name"": ""Alan"",
+ ""url"": ""http://www.google.com"",
+ ""role"": [
+ ""Admin""
+ ]
+ }
+ }
+}";
+ Assert.AreEqual(expected, arrayJsonText);
arrayXml = @"
@@ -399,8 +554,21 @@ namespace Newtonsoft.Json.Tests.Converters
arrayDoc = new XmlDocument();
arrayDoc.LoadXml(arrayXml);
- arrayJsonText = JsonConvert.SerializeXmlNode(arrayDoc);
- Assert.AreEqual(@"{""root"":{""person"":{""@id"":""1"",""name"":""Alan"",""url"":""http://www.google.com"",""role"":[""Admin1"",""Admin2""]}}}", arrayJsonText);
+ arrayJsonText = SerializeXmlNode(arrayDoc);
+ expected = @"{
+ ""root"": {
+ ""person"": {
+ ""@id"": ""1"",
+ ""name"": ""Alan"",
+ ""url"": ""http://www.google.com"",
+ ""role"": [
+ ""Admin1"",
+ ""Admin2""
+ ]
+ }
+ }
+}";
+ Assert.AreEqual(expected, arrayJsonText);
arrayXml = @"
@@ -413,58 +581,54 @@ namespace Newtonsoft.Json.Tests.Converters
arrayDoc = new XmlDocument();
arrayDoc.LoadXml(arrayXml);
- arrayJsonText = JsonConvert.SerializeXmlNode(arrayDoc);
- Assert.AreEqual(@"{""root"":{""person"":{""@id"":""1"",""name"":""Alan"",""url"":""http://www.google.com"",""role"":""Admin1""}}}", arrayJsonText);
+ 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 MultipleRootProperties()
+ public void MultipleRootPropertiesXmlDocument()
{
- string strJSON = @"{
-""count"": 773840,""photos"": [
-{
-""photo_id"": 532693,
-""photo_title"": ""Wheatfield in afternoon light"",
-""photo_url"": ""http://www.panoramio.com/photo/532693"",
-""photofileurl"": ""http://static2.bareka.com/photos/medium/532693.jpg"",
-""longitude"": 11.280727,
-""latitude"": 59.643198,
-""width"": 500,
-""height"": 333,
-""upload_date"": ""22 January 2007"",
-""owner_id"": 39160,
-""owner_name"": ""Snemann"",
-""owner_url"": ""http://www.panoramio.com/user/39160"",
-},
-{
-""photo_id"": 505229,
-""photo_title"": ""Etangs près de Dijon"",
-""photo_url"": ""http://www.panoramio.com/photo/505229"",
-""photofileurl"": ""http://static2.bareka.com/photos/medium/505229.jpg"",
-""longitude"": 5.168552,
-""latitude"": 47.312642,
-""width"": 350,
-""height"": 500,
-""upload_date"": ""20 January 2007"",
-""owner_id"": 78506,
-""owner_name"": ""Philippe Stoop"",
-""owner_url"": ""http://www.panoramio.com/user/78506""
-}
-]
-}";
+ string json = @"{""count"": 773840,""photos"": null}";
- XmlDocument newDoc = (XmlDocument)JsonConvert.DeserializeXmlNode(strJSON);
+ 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 strJSON = @"{""count"": 773840,""photos"": 773840}";
+ string json = @"{""count"": 773840,""photos"": 773840}";
- XmlDocument newDoc = (XmlDocument)JsonConvert.DeserializeXmlNode(strJSON, "myRoot");
+ 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]
@@ -483,9 +647,15 @@ namespace Newtonsoft.Json.Tests.Converters
]
}";
- XmlDocument newDoc = (XmlDocument)JsonConvert.DeserializeXmlNode(json, "myRoot");
+ XmlDocument newDoc = JsonConvert.DeserializeXmlNode(json, "myRoot");
Assert.AreEqual(@"assets/images/resized/0001/1070/11070v1-max-150x150.jpgassets/images/resized/0001/1070/11070v1-max-150x150.jpgassets/images/resized/0001/1070/11070v1-max-250x250.jpgassets/images/resized/0001/1070/11070v1-max-250x250.jpg", newDoc.InnerXml);
+
+#if !NET20
+ XDocument newXDoc = JsonConvert.DeserializeXNode(json, "myRoot");
+
+ Assert.AreEqual(@"assets/images/resized/0001/1070/11070v1-max-150x150.jpgassets/images/resized/0001/1070/11070v1-max-150x150.jpgassets/images/resized/0001/1070/11070v1-max-250x250.jpgassets/images/resized/0001/1070/11070v1-max-250x250.jpg", newXDoc.ToString(SaveOptions.DisableFormatting));
+#endif
}
[Test]
@@ -508,9 +678,15 @@ namespace Newtonsoft.Json.Tests.Converters
]
}";
- XmlDocument newDoc = (XmlDocument)JsonConvert.DeserializeXmlNode(json, "myRoot");
+ 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]
@@ -520,8 +696,10 @@ namespace Newtonsoft.Json.Tests.Converters
doc.LoadXml(@"O""Connor"); // i use "" so it will be easier to see the problem
- string json = JsonConvert.SerializeXmlNode(doc);
- Assert.AreEqual(@"{""name"":""O\""Connor""}", json);
+ string json = SerializeXmlNode(doc);
+ Assert.AreEqual(@"{
+ ""name"": ""O\""Connor""
+}", json);
}
[Test]
@@ -533,7 +711,7 @@ namespace Newtonsoft.Json.Tests.Converters
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
- string jsonText = JsonConvert.SerializeObject(doc, Formatting.Indented, new XmlNodeConverter());
+ string jsonText = SerializeXmlNode(doc);
string expected = @"{
""span"": {
@@ -548,7 +726,7 @@ namespace Newtonsoft.Json.Tests.Converters
Assert.AreEqual(expected, jsonText);
- XmlDocument newDoc = JsonConvert.DeserializeObject(jsonText, new XmlNodeConverter());
+ XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText);
Assert.AreEqual(@"Text", newDoc.InnerXml);
}
@@ -570,7 +748,7 @@ namespace Newtonsoft.Json.Tests.Converters
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
- string jsonText = JsonConvert.SerializeXmlNode(doc);
+ string jsonText = SerializeXmlNode(doc);
// {
// "?xml": {
// "@version": "1.0",
@@ -616,7 +794,7 @@ namespace Newtonsoft.Json.Tests.Converters
}
}", jsonText);
- XmlDocument newDoc = (XmlDocument)JsonConvert.DeserializeXmlNode(jsonText);
+ XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText);
Assert.AreEqual(doc.InnerXml, newDoc.InnerXml);
}
@@ -645,7 +823,7 @@ namespace Newtonsoft.Json.Tests.Converters
}
}";
- XmlDocument doc = (XmlDocument)JsonConvert.DeserializeXmlNode(json);
+ XmlDocument doc = (XmlDocument)DeserializeXmlNode(json);
//
//
//
diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs
index 771bdc56..099f5009 100644
--- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs
+++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs
@@ -48,6 +48,9 @@ using System.Runtime.Serialization;
using System.Globalization;
using Newtonsoft.Json.Utilities;
using System.Reflection;
+#if !NET20 && !SILVERLIGHT
+using System.Xml.Linq;
+#endif
namespace Newtonsoft.Json.Tests.Serialization
{
@@ -2715,12 +2718,14 @@ keyword such as type of business.""
json = JsonConvert.SerializeObject(myB, Formatting.Indented);
Assert.AreEqual(@"{
""BB_field1"": 4,
+ ""BB_field2"": 4,
""AA_field1"": 3,
""BB_property1"": 4,
""BB_property2"": 4,
""BB_property3"": 4,
""BB_property4"": 4,
""BB_property5"": 4,
+ ""BB_property7"": 4,
""AA_property1"": 3,
""AA_property2"": 3,
""AA_property3"": 3,
@@ -2734,6 +2739,7 @@ keyword such as type of business.""
AA myA = JsonConvert.DeserializeObject(
@"{
""AA_field1"": 2,
+ ""AA_field2"": 2,
""AA_property1"": 2,
""AA_property2"": 2,
""AA_property3"": 2,
@@ -2743,6 +2749,7 @@ keyword such as type of business.""
}");
Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetField("AA_field1", BindingFlags.Instance | BindingFlags.NonPublic), myA));
+ Assert.AreEqual(0, ReflectionUtils.GetMemberValue(typeof(AA).GetField("AA_field2", BindingFlags.Instance | BindingFlags.NonPublic), myA));
Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property1", BindingFlags.Instance | BindingFlags.NonPublic), myA));
Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property2", BindingFlags.Instance | BindingFlags.NonPublic), myA));
Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property3", BindingFlags.Instance | BindingFlags.NonPublic), myA));
@@ -2753,7 +2760,9 @@ keyword such as type of business.""
BB myB = JsonConvert.DeserializeObject(
@"{
""BB_field1"": 4,
+ ""BB_field2"": 4,
""AA_field1"": 3,
+ ""AA_field2"": 3,
""AA_property1"": 2,
""AA_property2"": 2,
""AA_property3"": 2,
@@ -2765,10 +2774,13 @@ keyword such as type of business.""
""BB_property3"": 3,
""BB_property4"": 3,
""BB_property5"": 3,
- ""BB_property6"": 3
+ ""BB_property6"": 3,
+ ""BB_property7"": 3,
+ ""BB_property8"": 3
}");
Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(AA).GetField("AA_field1", BindingFlags.Instance | BindingFlags.NonPublic), myB));
+ Assert.AreEqual(0, ReflectionUtils.GetMemberValue(typeof(AA).GetField("AA_field2", BindingFlags.Instance | BindingFlags.NonPublic), myB));
Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property1", BindingFlags.Instance | BindingFlags.NonPublic), myB));
Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property2", BindingFlags.Instance | BindingFlags.NonPublic), myB));
Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property3", BindingFlags.Instance | BindingFlags.NonPublic), myB));
@@ -2777,18 +2789,22 @@ keyword such as type of business.""
Assert.AreEqual(0, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property6", BindingFlags.Instance | BindingFlags.NonPublic), myB));
Assert.AreEqual(4, myB.BB_field1);
+ Assert.AreEqual(4, myB.BB_field2);
Assert.AreEqual(3, myB.BB_property1);
Assert.AreEqual(3, myB.BB_property2);
Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(BB).GetProperty("BB_property3", BindingFlags.Instance | BindingFlags.Public), myB));
Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(BB).GetProperty("BB_property4", BindingFlags.Instance | BindingFlags.NonPublic), myB));
Assert.AreEqual(0, myB.BB_property5);
Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(BB).GetProperty("BB_property6", BindingFlags.Instance | BindingFlags.Public), myB));
+ Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(BB).GetProperty("BB_property7", BindingFlags.Instance | BindingFlags.Public), myB));
+ Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(BB).GetProperty("BB_property8", BindingFlags.Instance | BindingFlags.Public), myB));
}
public class AA
{
[JsonProperty]
protected int AA_field1;
+ protected int AA_field2;
[JsonProperty]
protected int AA_property1 { get; set; }
[JsonProperty]
@@ -2807,6 +2823,7 @@ keyword such as type of business.""
public AA(int f)
{
AA_field1 = f;
+ AA_field2 = f;
AA_property1 = f;
AA_property2 = f;
AA_property3 = f;
@@ -2820,6 +2837,7 @@ keyword such as type of business.""
{
[JsonProperty]
public int BB_field1;
+ public int BB_field2;
[JsonProperty]
public int BB_property1 { get; set; }
[JsonProperty]
@@ -2830,6 +2848,9 @@ keyword such as type of business.""
private int BB_property4 { get; set; }
public int BB_property5 { get; private set; }
public int BB_property6 { private get; set; }
+ [JsonProperty]
+ public int BB_property7 { protected get; set; }
+ public int BB_property8 { protected get; set; }
public BB()
{
@@ -2839,13 +2860,84 @@ keyword such as type of business.""
: base(f)
{
BB_field1 = g;
+ BB_field2 = g;
BB_property1 = g;
BB_property2 = g;
BB_property3 = g;
BB_property4 = g;
BB_property5 = g;
BB_property6 = g;
+ BB_property7 = g;
+ BB_property8 = g;
}
}
+
+#if !NET20 && !SILVERLIGHT
+ public class XNodeTestObject
+ {
+ public XDocument Document { get; set; }
+ public XElement Element { get; set; }
+ }
+#endif
+
+#if !SILVERLIGHT
+ public class XmlNodeTestObject
+ {
+ public XmlDocument Document { get; set; }
+ }
+#endif
+
+#if !NET20 && !SILVERLIGHT
+ [Test]
+ public void SerializeDeserializeXNodeProperties()
+ {
+ XNodeTestObject testObject = new XNodeTestObject();
+ testObject.Document = XDocument.Parse("hehe, root");
+ testObject.Element = XElement.Parse(@"element");
+
+ string json = JsonConvert.SerializeObject(testObject, Formatting.Indented);
+ string expected = @"{
+ ""Document"": {
+ ""root"": ""hehe, root""
+ },
+ ""Element"": {
+ ""fifth"": {
+ ""@xmlns:json"": ""http://json.org"",
+ ""@json:Awesome"": ""true"",
+ ""#text"": ""element""
+ }
+ }
+}";
+ Assert.AreEqual(expected, json);
+
+ XNodeTestObject newTestObject = JsonConvert.DeserializeObject(json);
+ Assert.AreEqual(testObject.Document.ToString(), newTestObject.Document.ToString());
+ Assert.AreEqual(testObject.Element.ToString(), newTestObject.Element.ToString());
+
+ Assert.IsNull(newTestObject.Element.Parent);
+ }
+#endif
+
+#if !SILVERLIGHT
+ [Test]
+ public void SerializeDeserializeXmlNodeProperties()
+ {
+ XmlNodeTestObject testObject = new XmlNodeTestObject();
+ XmlDocument document = new XmlDocument();
+ document.LoadXml("hehe, root");
+ testObject.Document = document;
+
+ string json = JsonConvert.SerializeObject(testObject, Formatting.Indented);
+ string expected = @"{
+ ""Document"": {
+ ""root"": ""hehe, root""
+ }
+}";
+ Assert.AreEqual(expected, json);
+
+ XmlNodeTestObject newTestObject = JsonConvert.DeserializeObject(json);
+ Assert.AreEqual(testObject.Document.InnerXml, newTestObject.Document.InnerXml);
+ }
+#endif
}
}
\ No newline at end of file
diff --git a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs
index 6f8ab2db..ee5434eb 100644
--- a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs
+++ b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs
@@ -27,11 +27,703 @@
using System;
using System.Collections.Generic;
using System.Xml;
+#if !NET20
+using System.Xml.Linq;
+#endif
using Newtonsoft.Json.Utilities;
using System.Linq;
namespace Newtonsoft.Json.Converters
{
+ #region Wrappers
+ internal class XmlDocumentWrapper : XmlNodeWrapper, IXmlDocument
+ {
+ private XmlDocument _document;
+
+ public XmlDocumentWrapper(XmlDocument document)
+ : base(document)
+ {
+ _document = document;
+ }
+
+ public IXmlNode CreateComment(string data)
+ {
+ return new XmlNodeWrapper(_document.CreateComment(data));
+ }
+
+ public IXmlNode CreateTextNode(string text)
+ {
+ return new XmlNodeWrapper(_document.CreateTextNode(text));
+ }
+
+ public IXmlNode CreateCDataSection(string data)
+ {
+ return new XmlNodeWrapper(_document.CreateCDataSection(data));
+ }
+
+ public IXmlNode CreateWhitespace(string text)
+ {
+ return new XmlNodeWrapper(_document.CreateWhitespace(text));
+ }
+
+ public IXmlNode CreateSignificantWhitespace(string text)
+ {
+ return new XmlNodeWrapper(_document.CreateSignificantWhitespace(text));
+ }
+
+ public IXmlNode CreateXmlDeclaration(string version, string encoding, string standalone)
+ {
+ return new XmlNodeWrapper(_document.CreateXmlDeclaration(version, encoding, standalone));
+ }
+
+ public IXmlNode CreateProcessingInstruction(string target, string data)
+ {
+ return new XmlNodeWrapper(_document.CreateProcessingInstruction(target, data));
+ }
+
+ public IXmlElement CreateElement(string elementName)
+ {
+ return new XmlElementWrapper(_document.CreateElement(elementName));
+ }
+
+ public IXmlElement CreateElement(string qualifiedName, string namespaceURI)
+ {
+ return new XmlElementWrapper(_document.CreateElement(qualifiedName, namespaceURI));
+ }
+
+ public IXmlNode CreateAttribute(string name, string value)
+ {
+ XmlNodeWrapper attribute = new XmlNodeWrapper(_document.CreateAttribute(name));
+ attribute.Value = value;
+
+ return attribute;
+ }
+
+ public IXmlNode CreateAttribute(string qualifiedName, string namespaceURI, string value)
+ {
+ XmlNodeWrapper attribute = new XmlNodeWrapper(_document.CreateAttribute(qualifiedName, namespaceURI));
+ attribute.Value = value;
+
+ return attribute;
+ }
+
+ public IXmlElement DocumentElement
+ {
+ get
+ {
+ if (_document.DocumentElement == null)
+ return null;
+
+ return new XmlElementWrapper(_document.DocumentElement);
+ }
+ }
+ }
+
+ internal class XmlElementWrapper : XmlNodeWrapper, IXmlElement
+ {
+ private XmlElement _element;
+
+ public XmlElementWrapper(XmlElement element)
+ : base(element)
+ {
+ _element = element;
+ }
+
+ public void SetAttributeNode(IXmlNode attribute)
+ {
+ XmlNodeWrapper xmlAttributeWrapper = (XmlNodeWrapper)attribute;
+
+ _element.SetAttributeNode((XmlAttribute) xmlAttributeWrapper.WrappedNode);
+ }
+ }
+
+ internal class XmlDeclarationWrapper : XmlNodeWrapper, IXmlDeclaration
+ {
+ private XmlDeclaration _declaration;
+
+ public XmlDeclarationWrapper(XmlDeclaration declaration)
+ : base(declaration)
+ {
+ _declaration = declaration;
+ }
+
+ public string Version
+ {
+ get { return _declaration.Version; }
+ }
+
+ public string Encoding
+ {
+ get { return _declaration.Encoding; }
+ set { _declaration.Encoding = value; }
+ }
+
+ public string Standalone
+ {
+ get { return _declaration.Standalone; }
+ set { _declaration.Standalone = value; }
+ }
+ }
+
+ internal class XmlNodeWrapper : IXmlNode
+ {
+ private readonly XmlNode _node;
+
+ public XmlNodeWrapper(XmlNode node)
+ {
+ _node = node;
+ }
+
+ public object WrappedNode
+ {
+ get { return _node; }
+ }
+
+ public XmlNodeType NodeType
+ {
+ get { return _node.NodeType; }
+ }
+
+ public string Name
+ {
+ get { return _node.Name; }
+ }
+
+ public string LocalName
+ {
+ get { return _node.LocalName; }
+ }
+
+ public IList ChildNodes
+ {
+ get { return _node.ChildNodes.Cast().Select(n => WrapNode(n)).ToList(); }
+ }
+
+ private IXmlNode WrapNode(XmlNode node)
+ {
+ switch (node.NodeType)
+ {
+ case XmlNodeType.Element:
+ return new XmlElementWrapper((XmlElement) node);
+ case XmlNodeType.XmlDeclaration:
+ return new XmlDeclarationWrapper((XmlDeclaration) node);
+ default:
+ return new XmlNodeWrapper(node);
+ }
+ }
+
+ public IList Attributes
+ {
+ get
+ {
+ if (_node.Attributes == null)
+ return null;
+
+ return _node.Attributes.Cast().Select(a => WrapNode(a)).ToList();
+ }
+ }
+
+ public IXmlNode ParentNode
+ {
+ get
+ {
+ XmlNode node = (_node is XmlAttribute)
+ ? ((XmlAttribute) _node).OwnerElement
+ : _node.ParentNode;
+
+ if (node == null)
+ return null;
+
+ return WrapNode(node);
+ }
+ }
+
+ public string Value
+ {
+ get { return _node.Value; }
+ set { _node.Value = value; }
+ }
+
+ public IXmlNode AppendChild(IXmlNode newChild)
+ {
+ XmlNodeWrapper xmlNodeWrapper = (XmlNodeWrapper) newChild;
+ _node.AppendChild(xmlNodeWrapper._node);
+
+ return newChild;
+ }
+
+ public string Prefix
+ {
+ get { return _node.Prefix; }
+ }
+
+ public string NamespaceURI
+ {
+ get { return _node.NamespaceURI; }
+ }
+ }
+
+ internal interface IXmlDocument : IXmlNode
+ {
+ IXmlNode CreateComment(string text);
+ IXmlNode CreateTextNode(string text);
+ IXmlNode CreateCDataSection(string data);
+ IXmlNode CreateWhitespace(string text);
+ IXmlNode CreateSignificantWhitespace(string text);
+ IXmlNode CreateXmlDeclaration(string version, string encoding, string standalone);
+ IXmlNode CreateProcessingInstruction(string target, string data);
+ IXmlElement CreateElement(string elementName);
+ IXmlElement CreateElement(string qualifiedName, string namespaceURI);
+ IXmlNode CreateAttribute(string name, string value);
+ IXmlNode CreateAttribute(string qualifiedName, string namespaceURI, string value);
+
+ IXmlElement DocumentElement { get; }
+ }
+
+ internal interface IXmlDeclaration : IXmlNode
+ {
+ string Version { get; }
+ string Encoding { get; set; }
+ string Standalone { get; set; }
+ }
+
+ internal interface IXmlElement : IXmlNode
+ {
+ void SetAttributeNode(IXmlNode attribute);
+ }
+
+ internal interface IXmlNode
+ {
+ XmlNodeType NodeType { get; }
+ string LocalName { get; }
+ IList ChildNodes { get; }
+ IList Attributes { get; }
+ IXmlNode ParentNode { get; }
+ string Value { get; set; }
+ IXmlNode AppendChild(IXmlNode newChild);
+ string NamespaceURI { get; }
+ object WrappedNode { get; }
+ }
+
+ #if !NET20
+ internal class XDeclarationWrapper : XObjectWrapper, IXmlDeclaration
+ {
+ internal readonly XDeclaration _declaration;
+
+ public XDeclarationWrapper(XDeclaration declaration)
+ : base(null)
+ {
+ _declaration = declaration;
+ }
+
+ public override XmlNodeType NodeType
+ {
+ get { return XmlNodeType.XmlDeclaration; }
+ }
+
+ public string Version
+ {
+ get { return _declaration.Version; }
+ }
+
+ public string Encoding
+ {
+ get { return _declaration.Encoding; }
+ set { _declaration.Encoding = value; }
+ }
+
+ public string Standalone
+ {
+ get { return _declaration.Standalone; }
+ set { _declaration.Standalone = value; }
+ }
+ }
+
+ internal class XDocumentWrapper : XContainerWrapper, IXmlDocument
+ {
+ private XDocument Document
+ {
+ get { return (XDocument)WrappedNode; }
+ }
+
+ public XDocumentWrapper(XDocument document)
+ : base(document)
+ {
+ }
+
+ public override IList ChildNodes
+ {
+ get
+ {
+ IList childNodes = base.ChildNodes;
+
+ if (Document.Declaration != null)
+ childNodes.Insert(0, new XDeclarationWrapper(Document.Declaration));
+
+ return childNodes;
+ }
+ }
+
+ public IXmlNode CreateComment(string text)
+ {
+ return new XObjectWrapper(new XComment(text));
+ }
+
+ public IXmlNode CreateTextNode(string text)
+ {
+ return new XObjectWrapper(new XText(text));
+ }
+
+ public IXmlNode CreateCDataSection(string data)
+ {
+ return new XObjectWrapper(new XCData(data));
+ }
+
+ public IXmlNode CreateWhitespace(string text)
+ {
+ return new XObjectWrapper(new XText(text));
+ }
+
+ public IXmlNode CreateSignificantWhitespace(string text)
+ {
+ return new XObjectWrapper(new XText(text));
+ }
+
+ public IXmlNode CreateXmlDeclaration(string version, string encoding, string standalone)
+ {
+ return new XDeclarationWrapper(new XDeclaration(version, encoding, standalone));
+ }
+
+ public IXmlNode CreateProcessingInstruction(string target, string data)
+ {
+ return new XProcessingInstructionWrapper(new XProcessingInstruction(target, data));
+ }
+
+ public IXmlElement CreateElement(string elementName)
+ {
+ return new XElementWrapper(new XElement(elementName));
+ }
+
+ public IXmlElement CreateElement(string qualifiedName, string namespaceURI)
+ {
+ string localName = MiscellaneousUtils.GetLocalName(qualifiedName);
+ return new XElementWrapper(new XElement(XName.Get(localName, namespaceURI)));
+ }
+
+ public IXmlNode CreateAttribute(string name, string value)
+ {
+ return new XAttributeWrapper(new XAttribute(name, value));
+ }
+
+ public IXmlNode CreateAttribute(string qualifiedName, string namespaceURI, string value)
+ {
+ string localName = MiscellaneousUtils.GetLocalName(qualifiedName);
+ return new XAttributeWrapper(new XAttribute(XName.Get(localName, namespaceURI), value));
+ }
+
+ public IXmlElement DocumentElement
+ {
+ get
+ {
+ if (Document.Root == null)
+ return null;
+
+ return new XElementWrapper(Document.Root);
+ }
+ }
+
+ public override IXmlNode AppendChild(IXmlNode newChild)
+ {
+ XDeclarationWrapper declarationWrapper = newChild as XDeclarationWrapper;
+ if (declarationWrapper != null)
+ {
+ Document.Declaration = declarationWrapper._declaration;
+ return declarationWrapper;
+ }
+ else
+ {
+ return base.AppendChild(newChild);
+ }
+ }
+ }
+
+ internal class XTextWrapper : XObjectWrapper
+ {
+ private XText Text
+ {
+ get { return (XText)WrappedNode; }
+ }
+
+ public XTextWrapper(XText text)
+ : base(text)
+ {
+ }
+
+ public override string Value
+ {
+ get { return Text.Value; }
+ set { Text.Value = value; }
+ }
+
+ public override IXmlNode ParentNode
+ {
+ get
+ {
+ if (Text.Parent == null)
+ return null;
+
+ return XContainerWrapper.WrapNode(Text.Parent);
+ }
+ }
+ }
+
+ internal class XCommentWrapper : XObjectWrapper
+ {
+ private XComment Text
+ {
+ get { return (XComment)WrappedNode; }
+ }
+
+ public XCommentWrapper(XComment text)
+ : base(text)
+ {
+ }
+
+ public override string Value
+ {
+ get { return Text.Value; }
+ set { Text.Value = value; }
+ }
+
+ public override IXmlNode ParentNode
+ {
+ get
+ {
+ if (Text.Parent == null)
+ return null;
+
+ return XContainerWrapper.WrapNode(Text.Parent);
+ }
+ }
+ }
+
+ internal class XProcessingInstructionWrapper : XObjectWrapper
+ {
+ private XProcessingInstruction ProcessingInstruction
+ {
+ get { return (XProcessingInstruction)WrappedNode; }
+ }
+
+ public XProcessingInstructionWrapper(XProcessingInstruction processingInstruction)
+ : base(processingInstruction)
+ {
+ }
+
+ public override string LocalName
+ {
+ get { return ProcessingInstruction.Target; }
+ }
+
+ public override string Value
+ {
+ get { return ProcessingInstruction.Data; }
+ set { ProcessingInstruction.Data = value; }
+ }
+ }
+
+ internal class XContainerWrapper : XObjectWrapper
+ {
+ private XContainer Container
+ {
+ get { return (XContainer)WrappedNode; }
+ }
+
+ public XContainerWrapper(XContainer container)
+ : base(container)
+ {
+ }
+
+ public override IList ChildNodes
+ {
+ get { return Container.Nodes().Select(n => WrapNode(n)).ToList(); }
+ }
+
+ public override IXmlNode ParentNode
+ {
+ get
+ {
+ if (Container.Parent == null)
+ return null;
+
+ return WrapNode(Container.Parent);
+ }
+ }
+
+ internal static IXmlNode WrapNode(XObject node)
+ {
+ if (node is XDocument)
+ return new XDocumentWrapper((XDocument)node);
+ else if (node is XElement)
+ return new XElementWrapper((XElement)node);
+ else if (node is XContainer)
+ return new XContainerWrapper((XContainer)node);
+ else if (node is XProcessingInstruction)
+ return new XProcessingInstructionWrapper((XProcessingInstruction)node);
+ else if (node is XText)
+ return new XTextWrapper((XText)node);
+ else if (node is XComment)
+ return new XCommentWrapper((XComment)node);
+ else if (node is XAttribute)
+ return new XAttributeWrapper((XAttribute) node);
+ else
+ return new XObjectWrapper(node);
+ }
+
+ public override IXmlNode AppendChild(IXmlNode newChild)
+ {
+ Container.Add(newChild.WrappedNode);
+ return newChild;
+ }
+ }
+
+ internal class XObjectWrapper : IXmlNode
+ {
+ private readonly XObject _xmlObject;
+
+ public XObjectWrapper(XObject xmlObject)
+ {
+ _xmlObject = xmlObject;
+ }
+
+ public object WrappedNode
+ {
+ get { return _xmlObject; }
+ }
+
+ public virtual XmlNodeType NodeType
+ {
+ get { return _xmlObject.NodeType; }
+ }
+
+ public virtual string LocalName
+ {
+ get { return null; }
+ }
+
+ public virtual IList ChildNodes
+ {
+ get { return new List(); }
+ }
+
+ public virtual IList Attributes
+ {
+ get { return null; }
+ }
+
+ public virtual IXmlNode ParentNode
+ {
+ get { return null; }
+ }
+
+ public virtual string Value
+ {
+ get { return null; }
+ set { throw new InvalidOperationException(); }
+ }
+
+ public virtual IXmlNode AppendChild(IXmlNode newChild)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public virtual string NamespaceURI
+ {
+ get { return null; }
+ }
+ }
+
+ internal class XAttributeWrapper : XObjectWrapper
+ {
+ private XAttribute Attribute
+ {
+ get { return (XAttribute)WrappedNode; }
+ }
+
+ public XAttributeWrapper(XAttribute attribute)
+ : base(attribute)
+ {
+ }
+
+ public override string Value
+ {
+ get { return Attribute.Value; }
+ set { Attribute.Value = value; }
+ }
+
+ public override string LocalName
+ {
+ get { return Attribute.Name.LocalName; }
+ }
+
+ public override string NamespaceURI
+ {
+ get { return Attribute.Name.NamespaceName; }
+ }
+
+ public override IXmlNode ParentNode
+ {
+ get
+ {
+ if (Attribute.Parent == null)
+ return null;
+
+ return XContainerWrapper.WrapNode(Attribute.Parent);
+ }
+ }
+ }
+
+ internal class XElementWrapper : XContainerWrapper, IXmlElement
+ {
+ private XElement Element
+ {
+ get { return (XElement) WrappedNode; }
+ }
+
+ public XElementWrapper(XElement element)
+ : base(element)
+ {
+ }
+
+ public void SetAttributeNode(IXmlNode attribute)
+ {
+ XObjectWrapper wrapper = (XObjectWrapper)attribute;
+ Element.Add(wrapper.WrappedNode);
+ }
+
+ public override IList Attributes
+ {
+ get { return Element.Attributes().Select(a => new XAttributeWrapper(a)).Cast().ToList(); }
+ }
+
+ public override string Value
+ {
+ get { return Element.Value; }
+ set { Element.Value = value; }
+ }
+
+ public override string LocalName
+ {
+ get { return Element.Name.LocalName; }
+ }
+
+ public override string NamespaceURI
+ {
+ get { return Element.Name.NamespaceName; }
+ }
+ }
+#endif
+ #endregion
+
///
/// Converts an to and from JSON.
///
@@ -60,30 +752,83 @@ namespace Newtonsoft.Json.Converters
/// The value.
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
- XmlNode node = value as XmlNode;
+ IXmlNode node;
- if (node == null)
+ if (value is XmlNode)
+ node = new XmlNodeWrapper((XmlNode)value);
+#if !NET20
+ else if (value is XObject)
+ node = XContainerWrapper.WrapNode((XObject)value);
+#endif
+ else
throw new ArgumentException("Value must be an XmlNode", "value");
+ XmlNamespaceManager manager = new XmlNamespaceManager(new NameTable());
+ PushParentNamespaces(node, manager);
+
writer.WriteStartObject();
- SerializeNode(writer, node, true);
+ SerializeNode(writer, node, manager, true);
writer.WriteEndObject();
}
- private string GetPropertyName(XmlNode node)
+ private void PushParentNamespaces(IXmlNode node, XmlNamespaceManager manager)
+ {
+ List parentElements = null;
+
+ IXmlNode parent = node;
+ while ((parent = parent.ParentNode) != null)
+ {
+ if (parent.NodeType == XmlNodeType.Element)
+ {
+ if (parentElements == null)
+ parentElements = new List();
+
+ parentElements.Add(parent);
+ }
+ }
+
+ if (parentElements != null)
+ {
+ parentElements.Reverse();
+
+ foreach (IXmlNode parentElement in parentElements)
+ {
+ manager.PushScope();
+ foreach (IXmlNode attribute in parentElement.Attributes)
+ {
+ if (attribute.NamespaceURI == "http://www.w3.org/2000/xmlns/" && attribute.LocalName != "xmlns")
+ manager.AddNamespace(attribute.LocalName, attribute.Value);
+ }
+ }
+ }
+ }
+
+ private string ResolveFullName(IXmlNode node, XmlNamespaceManager manager)
+ {
+ string prefix = (node.NamespaceURI == null || (node.LocalName == "xmlns" && node.NamespaceURI == "http://www.w3.org/2000/xmlns/"))
+ ? null
+ : manager.LookupPrefix(node.NamespaceURI);
+
+ if (!string.IsNullOrEmpty(prefix))
+ return prefix + ":" + node.LocalName;
+ else
+ return node.LocalName;
+ }
+
+ private string GetPropertyName(IXmlNode node, XmlNamespaceManager manager)
{
switch (node.NodeType)
{
case XmlNodeType.Attribute:
- return "@" + node.Name;
+ return "@" + ResolveFullName(node, manager);
case XmlNodeType.CDATA:
return CDataName;
case XmlNodeType.Comment:
return CommentName;
case XmlNodeType.Element:
- return node.Name;
+ return ResolveFullName(node, manager);
case XmlNodeType.ProcessingInstruction:
- return "?" + node.Name;
+ return "?" + ResolveFullName(node, manager);
case XmlNodeType.XmlDeclaration:
return DeclarationName;
case XmlNodeType.SignificantWhitespace:
@@ -97,20 +842,20 @@ namespace Newtonsoft.Json.Converters
}
}
- private void SerializeGroupedNodes(JsonWriter writer, XmlNode node)
+ private void SerializeGroupedNodes(JsonWriter writer, IXmlNode node, XmlNamespaceManager manager)
{
// group nodes together by name
- Dictionary> nodesGroupedByName = new Dictionary>();
+ Dictionary> nodesGroupedByName = new Dictionary>();
for (int i = 0; i < node.ChildNodes.Count; i++)
{
- XmlNode childNode = node.ChildNodes[i];
- string nodeName = GetPropertyName(childNode);
+ IXmlNode childNode = node.ChildNodes[i];
+ string nodeName = GetPropertyName(childNode, manager);
- List nodes;
+ List nodes;
if (!nodesGroupedByName.TryGetValue(nodeName, out nodes))
{
- nodes = new List();
+ nodes = new List();
nodesGroupedByName.Add(nodeName, nodes);
}
@@ -119,15 +864,17 @@ namespace Newtonsoft.Json.Converters
// loop through grouped nodes. write single name instances as normal,
// write multiple names together in an array
- foreach (KeyValuePair> nodeNameGroup in nodesGroupedByName)
+ foreach (KeyValuePair> nodeNameGroup in nodesGroupedByName)
{
- List groupedNodes = nodeNameGroup.Value;
+ List groupedNodes = nodeNameGroup.Value;
bool writeArray;
if (groupedNodes.Count == 1)
{
- XmlNode singleNode = groupedNodes[0];
- XmlAttribute jsonArrayAttribute = (singleNode.Attributes != null) ? singleNode.Attributes["Array", JsonNamespaceUri] : null;
+ IXmlNode singleNode = groupedNodes[0];
+ IXmlNode jsonArrayAttribute = (singleNode.Attributes != null)
+ ? singleNode.Attributes.SingleOrDefault(a => a.LocalName == "Array" && a.NamespaceURI == JsonNamespaceUri)
+ : null;
if (jsonArrayAttribute != null)
writeArray = XmlConvert.ToBoolean(jsonArrayAttribute.Value);
else
@@ -140,7 +887,7 @@ namespace Newtonsoft.Json.Converters
if (!writeArray)
{
- SerializeNode(writer, groupedNodes[0], true);
+ SerializeNode(writer, groupedNodes[0], manager, true);
}
else
{
@@ -150,7 +897,7 @@ namespace Newtonsoft.Json.Converters
for (int i = 0; i < groupedNodes.Count; i++)
{
- SerializeNode(writer, groupedNodes[i], false);
+ SerializeNode(writer, groupedNodes[i], manager, false);
}
writer.WriteEndArray();
@@ -158,17 +905,29 @@ namespace Newtonsoft.Json.Converters
}
}
- private void SerializeNode(JsonWriter writer, XmlNode node, bool writePropertyName)
+ private void SerializeNode(JsonWriter writer, IXmlNode node, XmlNamespaceManager manager, bool writePropertyName)
{
switch (node.NodeType)
{
case XmlNodeType.Document:
case XmlNodeType.DocumentFragment:
- SerializeGroupedNodes(writer, node);
+ SerializeGroupedNodes(writer, node, manager);
break;
case XmlNodeType.Element:
+ foreach (IXmlNode attribute in node.Attributes)
+ {
+ if (attribute.NamespaceURI == "http://www.w3.org/2000/xmlns/")
+ {
+ string prefix = (attribute.LocalName != "xmlns")
+ ? attribute.LocalName
+ : string.Empty;
+
+ manager.AddNamespace(prefix, attribute.Value);
+ }
+ }
+
if (writePropertyName)
- writer.WritePropertyName(node.Name);
+ writer.WritePropertyName(GetPropertyName(node, manager));
if (ValueAttributes(node.Attributes).Count() == 0 && node.ChildNodes.Count == 1
&& node.ChildNodes[0].NodeType == XmlNodeType.Text)
@@ -181,30 +940,16 @@ namespace Newtonsoft.Json.Converters
// empty element
writer.WriteNull();
}
- else if (node.ChildNodes.OfType().Where(x => x.Name.StartsWith("-")).Count() > 1)
- {
- XmlElement constructorValueElement = node.ChildNodes.OfType().Where(x => x.Name.StartsWith("-")).First();
- string constructorName = constructorValueElement.Name.Substring(1);
-
- writer.WriteStartConstructor(constructorName);
-
- for (int i = 0; i < node.ChildNodes.Count; i++)
- {
- SerializeNode(writer, node.ChildNodes[i], false);
- }
-
- writer.WriteEndConstructor();
- }
else
{
writer.WriteStartObject();
for (int i = 0; i < node.Attributes.Count; i++)
{
- SerializeNode(writer, node.Attributes[i], true);
+ SerializeNode(writer, node.Attributes[i], manager, true);
}
- SerializeGroupedNodes(writer, node);
+ SerializeGroupedNodes(writer, node, manager);
writer.WriteEndObject();
}
@@ -220,18 +965,18 @@ namespace Newtonsoft.Json.Converters
case XmlNodeType.ProcessingInstruction:
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
- if (node.Prefix == "xmlns" && node.Value == JsonNamespaceUri)
+ if (node.NamespaceURI == "http://www.w3.org/2000/xmlns/" && node.Value == JsonNamespaceUri)
break;
else if (node.NamespaceURI == JsonNamespaceUri)
break;
if (writePropertyName)
- writer.WritePropertyName(GetPropertyName(node));
+ writer.WritePropertyName(GetPropertyName(node, manager));
writer.WriteValue(node.Value);
break;
case XmlNodeType.XmlDeclaration:
- XmlDeclaration declaration = (XmlDeclaration)node;
- writer.WritePropertyName(GetPropertyName(node));
+ IXmlDeclaration declaration = (IXmlDeclaration)node;
+ writer.WritePropertyName(GetPropertyName(node, manager));
writer.WriteStartObject();
if (!string.IsNullOrEmpty(declaration.Version))
@@ -268,24 +1013,40 @@ namespace Newtonsoft.Json.Converters
/// The object value.
public override object ReadJson(JsonReader reader, Type objectType, JsonSerializer serializer)
{
- // maybe have CanReader and a CanWrite methods so this sort of test wouldn't be necessary
- if (objectType != typeof(XmlDocument))
- throw new JsonSerializationException("XmlNodeConverter only supports deserializing XmlDocuments");
+ XmlNamespaceManager manager = new XmlNamespaceManager(new NameTable());
+ IXmlDocument document;
+ IXmlNode rootNode;
- XmlDocument document = new XmlDocument();
- XmlNamespaceManager manager = new XmlNamespaceManager(document.NameTable);
+ if (typeof (XmlNode).IsAssignableFrom(objectType))
+ {
+ if (objectType != typeof (XmlDocument))
+ throw new JsonSerializationException("XmlNodeConverter only supports deserializing XmlDocuments");
+
+ XmlDocument d = new XmlDocument();
+ document = new XmlDocumentWrapper(d);
+ rootNode = document;
+ }
+#if !NET20
+ else if (typeof(XObject).IsAssignableFrom(objectType))
+ {
+ if (objectType != typeof (XDocument) && objectType != typeof (XElement))
+ throw new JsonSerializationException("XmlNodeConverter only supports deserializing XDocument or XElement.");
+
+ XDocument d = new XDocument();
+ document = new XDocumentWrapper(d);
+ rootNode = document;
+ }
+#endif
+ else
+ {
+ throw new JsonSerializationException("Unexpected type when converting XML: " + objectType);
+ }
- XmlNode rootNode;
-
if (!string.IsNullOrEmpty(DeserializeRootElementName))
{
rootNode = document.CreateElement(DeserializeRootElementName);
document.AppendChild(rootNode);
}
- else
- {
- rootNode = document;
- }
if (reader.TokenType != JsonToken.StartObject)
throw new JsonSerializationException("XmlNodeConverter can only convert JSON that begins with an object.");
@@ -294,10 +1055,20 @@ namespace Newtonsoft.Json.Converters
DeserializeNode(reader, document, manager, rootNode);
- return document;
+#if !NET20
+ if (objectType == typeof(XElement))
+ {
+ XElement element = (XElement)document.DocumentElement.WrappedNode;
+ element.Remove();
+
+ return element;
+ }
+#endif
+
+ return document.WrappedNode;
}
- private void DeserializeValue(JsonReader reader, XmlDocument document, XmlNamespaceManager manager, string propertyName, XmlNode currentNode)
+ private void DeserializeValue(JsonReader reader, IXmlDocument document, XmlNamespaceManager manager, string propertyName, IXmlNode currentNode)
{
switch (propertyName)
{
@@ -343,12 +1114,12 @@ namespace Newtonsoft.Json.Converters
}
}
- XmlDeclaration declaration = document.CreateXmlDeclaration(version, encoding, standalone);
+ IXmlNode declaration = document.CreateXmlDeclaration(version, encoding, standalone);
currentNode.AppendChild(declaration);
}
else
{
- XmlProcessingInstruction instruction = document.CreateProcessingInstruction(propertyName.Substring(1), reader.Value.ToString());
+ IXmlNode instruction = document.CreateProcessingInstruction(propertyName.Substring(1), reader.Value.ToString());
currentNode.AppendChild(instruction);
}
}
@@ -357,12 +1128,13 @@ namespace Newtonsoft.Json.Converters
// deserialize xml element
bool finishedAttributes = false;
bool finishedElement = false;
- string elementPrefix = GetPrefix(propertyName);
+ string elementPrefix = MiscellaneousUtils.GetPrefix(propertyName);
+
Dictionary attributeNameValues = new Dictionary();
if (reader.TokenType == JsonToken.StartArray)
{
- XmlElement nestedArrayElement = CreateElement(propertyName, document, elementPrefix, manager);
+ IXmlElement nestedArrayElement = CreateElement(propertyName, document, elementPrefix, manager);
currentNode.AppendChild(nestedArrayElement);
@@ -421,20 +1193,18 @@ namespace Newtonsoft.Json.Converters
// have to wait until attributes have been parsed before creating element
// attributes may contain namespace info used by the element
- XmlElement element = CreateElement(propertyName, document, elementPrefix, manager);
+ IXmlElement element = CreateElement(propertyName, document, elementPrefix, manager);
currentNode.AppendChild(element);
// add attributes to newly created element
foreach (KeyValuePair nameValue in attributeNameValues)
{
- string attributePrefix = GetPrefix(nameValue.Key);
+ string attributePrefix = MiscellaneousUtils.GetPrefix(nameValue.Key);
- XmlAttribute attribute = (!string.IsNullOrEmpty(attributePrefix))
- ? document.CreateAttribute(nameValue.Key, manager.LookupNamespace(attributePrefix))
- : document.CreateAttribute(nameValue.Key);
-
- attribute.Value = nameValue.Value;
+ IXmlNode attribute = (!string.IsNullOrEmpty(attributePrefix))
+ ? document.CreateAttribute(nameValue.Key, manager.LookupNamespace(attributePrefix), nameValue.Value)
+ : document.CreateAttribute(nameValue.Key, nameValue.Value);
element.SetAttributeNode(attribute);
}
@@ -481,14 +1251,14 @@ namespace Newtonsoft.Json.Converters
}
}
- private XmlElement CreateElement(string elementName, XmlDocument document, string elementPrefix, XmlNamespaceManager manager)
+ private IXmlElement CreateElement(string elementName, IXmlDocument document, string elementPrefix, XmlNamespaceManager manager)
{
return (!string.IsNullOrEmpty(elementPrefix))
? document.CreateElement(elementName, manager.LookupNamespace(elementPrefix))
: document.CreateElement(elementName);
}
- private void DeserializeNode(JsonReader reader, XmlDocument document, XmlNamespaceManager manager, XmlNode currentNode)
+ private void DeserializeNode(JsonReader reader, IXmlDocument document, XmlNamespaceManager manager, IXmlNode currentNode)
{
do
{
@@ -518,7 +1288,7 @@ namespace Newtonsoft.Json.Converters
while (reader.Read() && reader.TokenType != JsonToken.EndConstructor)
{
- DeserializeValue(reader, document, manager, "-" + constructorName, currentNode);
+ DeserializeValue(reader, document, manager, constructorName, currentNode);
}
break;
case JsonToken.Comment:
@@ -559,19 +1329,9 @@ namespace Newtonsoft.Json.Converters
return false;
}
- private string GetPrefix(string qualifiedName)
+ private IEnumerable ValueAttributes(IEnumerable c)
{
- int colonPosition = qualifiedName.IndexOf(':');
-
- if ((colonPosition == -1 || colonPosition == 0) || (qualifiedName.Length - 1) == colonPosition)
- return string.Empty;
- else
- return qualifiedName.Substring(0, colonPosition);
- }
-
- private IEnumerable ValueAttributes(XmlAttributeCollection c)
- {
- return c.OfType().Where(a => a.NamespaceURI != JsonNamespaceUri);
+ return c.Where(a => a.NamespaceURI != JsonNamespaceUri);
}
#endregion
@@ -584,7 +1344,14 @@ namespace Newtonsoft.Json.Converters
///
public override bool CanConvert(Type valueType)
{
- return typeof(XmlNode).IsAssignableFrom(valueType);
+ if (typeof(XmlNode).IsAssignableFrom(valueType))
+ return true;
+#if !NET20
+ if (typeof(XObject).IsAssignableFrom(valueType))
+ return true;
+#endif
+
+ return false;
}
}
}
diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs
index 45b6a533..ebdf8697 100644
--- a/Src/Newtonsoft.Json/JsonConvert.cs
+++ b/Src/Newtonsoft.Json/JsonConvert.cs
@@ -30,6 +30,9 @@ using Newtonsoft.Json.Utilities;
using System.Xml;
using Newtonsoft.Json.Converters;
using System.Text;
+#if !NET20 && !SILVERLIGHT
+using System.Xml.Linq;
+#endif
namespace Newtonsoft.Json
{
@@ -735,6 +738,20 @@ namespace Newtonsoft.Json
}
}
+#if !NET20 && !SILVERLIGHT
+ public static string SerializeXNode(XObject node)
+ {
+ return SerializeXNode(node, Formatting.None);
+ }
+
+ public static string SerializeXNode(XObject node, Formatting formatting)
+ {
+ XmlNodeConverter converter = new XmlNodeConverter();
+
+ return SerializeObject(node, formatting, converter);
+ }
+#endif
+
#if !SILVERLIGHT
///
/// Serializes the XML node to a JSON string.
@@ -742,10 +759,21 @@ namespace Newtonsoft.Json
/// The node to serialize.
/// A JSON string of the XmlNode.
public static string SerializeXmlNode(XmlNode node)
+ {
+ return SerializeXmlNode(node, Formatting.None);
+ }
+
+ ///
+ /// Serializes the XML node to a JSON string.
+ ///
+ /// The node to serialize.
+ /// Indicates how the output is formatted.
+ /// A JSON string of the XmlNode.
+ public static string SerializeXmlNode(XmlNode node, Formatting formatting)
{
XmlNodeConverter converter = new XmlNodeConverter();
- return SerializeObject(node, converter);
+ return SerializeObject(node, formatting, converter);
}
///
@@ -753,7 +781,7 @@ namespace Newtonsoft.Json
///
/// The JSON string.
/// The deserialized XmlNode
- public static XmlNode DeserializeXmlNode(string value)
+ public static XmlDocument DeserializeXmlNode(string value)
{
return DeserializeXmlNode(value, null);
}
@@ -764,13 +792,39 @@ namespace Newtonsoft.Json
/// The JSON string.
/// The name of the root element to append when deserializing.
/// The deserialized XmlNode
- public static XmlNode DeserializeXmlNode(string value, string deserializeRootElementName)
+ public static XmlDocument DeserializeXmlNode(string value, string deserializeRootElementName)
{
XmlNodeConverter converter = new XmlNodeConverter();
converter.DeserializeRootElementName = deserializeRootElementName;
return (XmlDocument)DeserializeObject(value, typeof(XmlDocument), converter);
}
+
+#if !NET20
+ ///
+ /// Deserializes the XNode from a JSON string.
+ ///
+ /// The JSON string.
+ /// The deserialized XmlNode
+ public static XDocument DeserializeXNode(string value)
+ {
+ return DeserializeXNode(value, null);
+ }
+
+ ///
+ /// Deserializes the XmlNode from a JSON string nested in a root elment.
+ ///
+ /// The JSON string.
+ /// The name of the root element to append when deserializing.
+ /// The deserialized XmlNode
+ public static XDocument DeserializeXNode(string value, string deserializeRootElementName)
+ {
+ XmlNodeConverter converter = new XmlNodeConverter();
+ converter.DeserializeRootElementName = deserializeRootElementName;
+
+ return (XDocument)DeserializeObject(value, typeof(XDocument), converter);
+ }
+#endif
#endif
}
}
\ No newline at end of file
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj
index f9512fbc..458e9587 100644
--- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj
+++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj
@@ -75,6 +75,9 @@
+
+ 3.5
+
diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs
index 860e9fad..1d344ad4 100644
--- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs
+++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs
@@ -50,6 +50,7 @@ namespace Newtonsoft.Json.Serialization
#endif
new BinaryConverter(),
#if !SILVERLIGHT
+ new XmlNodeConverter(),
new DataSetConverter(),
new DataTableConverter()
#endif
diff --git a/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs b/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs
index d21b6f10..b6665c92 100644
--- a/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs
+++ b/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs
@@ -98,5 +98,39 @@ namespace Newtonsoft.Json.Utilities
return true;
}
+
+ public static string GetPrefix(string qualifiedName)
+ {
+ string prefix;
+ string localName;
+ GetQualifiedNameParts(qualifiedName, out prefix, out localName);
+
+ return prefix;
+ }
+
+ public static string GetLocalName(string qualifiedName)
+ {
+ string prefix;
+ string localName;
+ GetQualifiedNameParts(qualifiedName, out prefix, out localName);
+
+ return localName;
+ }
+
+ public static void GetQualifiedNameParts(string qualifiedName, out string prefix, out string localName)
+ {
+ int colonPosition = qualifiedName.IndexOf(':');
+
+ if ((colonPosition == -1 || colonPosition == 0) || (qualifiedName.Length - 1) == colonPosition)
+ {
+ prefix = null;
+ localName = qualifiedName;
+ }
+ else
+ {
+ prefix = qualifiedName.Substring(0, colonPosition);
+ localName = qualifiedName.Substring(colonPosition + 1);
+ }
+ }
}
}
\ No newline at end of file