diff --git a/RestSharp.Tests/RestSharp.Tests.csproj b/RestSharp.Tests/RestSharp.Tests.csproj
index 2dc5e58c7..2bfe13443 100644
--- a/RestSharp.Tests/RestSharp.Tests.csproj
+++ b/RestSharp.Tests/RestSharp.Tests.csproj
@@ -80,6 +80,8 @@
+
+
@@ -103,7 +105,7 @@
-
+
diff --git a/RestSharp.Tests/SampleClasses/GoogleWeatherWithAttributes.cs b/RestSharp.Tests/SampleClasses/GoogleWeatherWithAttributes.cs
new file mode 100644
index 000000000..1b0fc889b
--- /dev/null
+++ b/RestSharp.Tests/SampleClasses/GoogleWeatherWithAttributes.cs
@@ -0,0 +1,60 @@
+using System.Collections.Generic;
+using RestSharp.Deserializers;
+
+namespace RestSharp.Tests.SampleClasses
+{
+ public class GoogleWeatherApi
+ {
+ public string Version { get; set; }
+ public GoogleWeather Weather { get; set; }
+ }
+
+ public class GoogleWeather : List
+ {
+ public string ModuleId { get; set; }
+ public string TabId { get; set; }
+ public string MobileRow { get; set; }
+ public string MobileZipped { get; set; }
+ public string Row { get; set; }
+ public string Section { get; set; }
+
+ [DeserializeAs(Name = "forecast_information")]
+ public ForecastInformation Forecast { get; set; }
+
+ [DeserializeAs(Name = "current_conditions")]
+ public CurrentConditions Current { get; set; }
+ }
+
+ public class GoogleDataElement
+ {
+ public string Data { get; set; }
+ }
+
+ public class ForecastInformation
+ {
+ public GoogleDataElement City { get; set; }
+ public GoogleDataElement PostalCode { get; set; }
+ public GoogleDataElement ForecastDate { get; set; }
+ public GoogleDataElement UnitSystem { get; set; }
+ }
+
+ public class CurrentConditions
+ {
+ public GoogleDataElement Condition { get; set; }
+ public GoogleDataElement TempC { get; set; }
+ public GoogleDataElement Humidity { get; set; }
+ public GoogleDataElement Icon { get; set; }
+ public GoogleDataElement WindCondition { get; set; }
+ }
+
+ public class ForecastConditions
+ {
+ public GoogleDataElement DayOfWeek { get; set; }
+ public GoogleDataElement Condition { get; set; }
+ public GoogleDataElement Low { get; set; }
+ public GoogleDataElement High { get; set; }
+ public GoogleDataElement Icon { get; set; }
+ }
+
+
+}
diff --git a/RestSharp.Tests/XmlAttributeDeserializerTests.cs b/RestSharp.Tests/XmlAttributeDeserializerTests.cs
new file mode 100644
index 000000000..0cfc0e26b
--- /dev/null
+++ b/RestSharp.Tests/XmlAttributeDeserializerTests.cs
@@ -0,0 +1,958 @@
+#region License
+// Copyright 2010 John Sheehan
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#endregion
+
+using System;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Xml.Linq;
+using RestSharp.Deserializers;
+using Xunit;
+using RestSharp.Tests.SampleClasses;
+using System.Collections.Generic;
+
+namespace RestSharp.Tests
+{
+ public class XmlAttributeDeserializerTests
+ {
+ private const string GuidString = "AC1FC4BC-087A-4242-B8EE-C53EBE9887A5";
+ private string SampleDataPath = Path.Combine(Environment.CurrentDirectory, "SampleData");
+
+ private string PathFor(string sampleFile)
+ {
+ return Path.Combine(SampleDataPath, sampleFile);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Lists_of_Simple_Types()
+ {
+ var xmlpath = PathFor("xmllists.xml");
+ var doc = XDocument.Load(xmlpath);
+
+ var xml = new XmlAttributeDeserializer();
+ var output = xml.Deserialize(new RestResponse() { Content = doc.ToString() });
+
+ Assert.NotEmpty(output.Names);
+ Assert.NotEmpty(output.Numbers);
+ Assert.False(output.Names[0].Length == 0);
+ Assert.False(output.Numbers.Sum() == 0);
+ }
+
+ [Fact]
+ public void Can_Deserialize_To_List_Inheritor_From_Custom_Root_With_Attributes()
+ {
+ var xmlpath = PathFor("ListWithAttributes.xml");
+ var doc = XDocument.Load(xmlpath);
+
+ var xml = new XmlAttributeDeserializer();
+ xml.RootElement = "Calls";
+ var output = xml.Deserialize(new RestResponse { Content = doc.ToString() });
+
+ Assert.Equal(3, output.NumPages);
+ Assert.NotEmpty(output);
+ Assert.Equal(2, output.Count);
+ }
+
+ [Fact]
+ public void Can_Deserialize_To_Standalone_List_Without_Matching_Class_Case()
+ {
+ var xmlpath = PathFor("InlineListSample.xml");
+ var doc = XDocument.Load(xmlpath);
+
+ var xml = new XmlAttributeDeserializer();
+ var output = xml.Deserialize>(new RestResponse { Content = doc.ToString() });
+
+ Assert.NotEmpty(output);
+ Assert.Equal(4, output.Count);
+ }
+
+ [Fact]
+ public void Can_Deserialize_To_Standalone_List_With_Matching_Class_Case()
+ {
+ var xmlpath = PathFor("InlineListSample.xml");
+ var doc = XDocument.Load(xmlpath);
+
+ var xml = new XmlAttributeDeserializer();
+ var output = xml.Deserialize>(new RestResponse { Content = doc.ToString() });
+
+ Assert.NotEmpty(output);
+ Assert.Equal(4, output.Count);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Directly_To_Lists_Off_Root_Element()
+ {
+ var xmlpath = PathFor("directlists.xml");
+ var doc = XDocument.Load(xmlpath);
+
+ var xml = new XmlAttributeDeserializer();
+ var output = xml.Deserialize>(new RestResponse { Content = doc.ToString() });
+
+ Assert.NotEmpty(output);
+ Assert.Equal(2, output.Count);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Parentless_aka_Inline_List_Items_Without_Matching_Class_Name()
+ {
+ var xmlpath = PathFor("InlineListSample.xml");
+ var doc = XDocument.Load(xmlpath);
+
+ var xml = new XmlAttributeDeserializer();
+ var output = xml.Deserialize(new RestResponse { Content = doc.ToString() });
+
+ Assert.NotEmpty(output.Images);
+ Assert.Equal(4, output.Images.Count);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Parentless_aka_Inline_List_Items_With_Matching_Class_Name()
+ {
+ var xmlpath = PathFor("InlineListSample.xml");
+ var doc = XDocument.Load(xmlpath);
+
+ var xml = new XmlAttributeDeserializer();
+ var output = xml.Deserialize(new RestResponse { Content = doc.ToString() });
+
+ Assert.NotEmpty(output.images);
+ Assert.Equal(4, output.images.Count);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Parentless_aka_Inline_List_Items_With_Matching_Class_Name_With_Additional_Property()
+ {
+ var xmlpath = PathFor("InlineListSample.xml");
+ var doc = XDocument.Load(xmlpath);
+
+ var xml = new XmlAttributeDeserializer();
+ var output = xml.Deserialize(new RestResponse { Content = doc.ToString() });
+
+ Assert.Equal(4, output.Count);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Nested_List_Items_Without_Matching_Class_Name()
+ {
+ var xmlpath = PathFor("NestedListSample.xml");
+ var doc = XDocument.Load(xmlpath);
+
+ var xml = new XmlAttributeDeserializer();
+ var output = xml.Deserialize(new RestResponse { Content = doc.ToString() });
+
+ Assert.NotEmpty(output.Images);
+ Assert.Equal(4, output.Images.Count);
+ }
+
+
+ [Fact]
+ public void Can_Deserialize_Nested_List_Items_With_Matching_Class_Name()
+ {
+ var xmlpath = PathFor("NestedListSample.xml");
+ var doc = XDocument.Load(xmlpath);
+
+ var xml = new XmlAttributeDeserializer();
+ var output = xml.Deserialize(new RestResponse { Content = doc.ToString() });
+
+ Assert.NotEmpty(output.images);
+ Assert.Equal(4, output.images.Count);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Nested_List_Without_Elements_To_Empty_List()
+ {
+ var doc = CreateXmlWithEmptyNestedList();
+
+ var xml = new XmlAttributeDeserializer();
+ var output = xml.Deserialize(new RestResponse { Content = doc });
+
+ Assert.NotNull(output.images);
+ Assert.NotNull(output.Images);
+ Assert.Empty(output.images);
+ Assert.Empty(output.Images);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Inline_List_Without_Elements_To_Empty_List()
+ {
+ var doc = CreateXmlWithEmptyInlineList();
+
+ var xml = new XmlAttributeDeserializer();
+ var output = xml.Deserialize(new RestResponse { Content = doc });
+
+ Assert.NotNull(output.images);
+ Assert.NotNull(output.Images);
+ Assert.Empty(output.images);
+ Assert.Empty(output.Images);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Empty_Elements_to_Nullable_Values()
+ {
+ var doc = CreateXmlWithNullValues();
+
+ var xml = new XmlAttributeDeserializer();
+ var output = xml.Deserialize(new RestResponse { Content = doc });
+
+ Assert.Null(output.Id);
+ Assert.Null(output.StartDate);
+ Assert.Null(output.UniqueId);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Elements_to_Nullable_Values()
+ {
+ var culture = CultureInfo.InvariantCulture;
+ var doc = CreateXmlWithoutEmptyValues(culture);
+ var xml = new XmlAttributeDeserializer() {Culture = culture};
+ var output = xml.Deserialize(new RestResponse { Content = doc });
+
+ Assert.NotNull(output.Id);
+ Assert.NotNull(output.StartDate);
+ Assert.NotNull(output.UniqueId);
+
+ Assert.Equal(123, output.Id);
+ Assert.Equal(new DateTime(2010, 2, 21, 9, 35, 00), output.StartDate);
+ Assert.Equal(new Guid(GuidString), output.UniqueId);
+ }
+
+ [Fact]
+ public void Can_Deserialize_TimeSpan()
+ {
+ var culture = CultureInfo.InvariantCulture;
+ var doc = new XDocument(culture);
+
+ TimeSpan? nullTimespan = null;
+ TimeSpan? nullValueTimeSpan = new TimeSpan(21, 30, 7);
+
+ var root = new XElement("Person");
+ root.Add(new XElement("Tick", new TimeSpan(468006)));
+ root.Add(new XElement("Millisecond", new TimeSpan(0, 0, 0, 0, 125)));
+ root.Add(new XElement("Second", new TimeSpan(0, 0, 8)));
+ root.Add(new XElement("Minute", new TimeSpan(0, 55, 2)));
+ root.Add(new XElement("Hour", new TimeSpan(21, 30, 7)));
+ root.Add(new XElement("NullableWithoutValue", nullTimespan));
+ root.Add(new XElement("NullableWithValue", nullValueTimeSpan));
+
+ doc.Add(root);
+
+ var response = new RestResponse { Content = doc.ToString() };
+
+ var d = new XmlAttributeDeserializer()
+ {
+ Culture = culture,
+ };
+ var payload = d.Deserialize(response);
+ Assert.Equal(new TimeSpan(468006), payload.Tick);
+ Assert.Equal(new TimeSpan(0, 0, 0, 0, 125), payload.Millisecond);
+ Assert.Equal(new TimeSpan(0, 0, 8), payload.Second);
+ Assert.Equal(new TimeSpan(0, 55, 2), payload.Minute);
+ Assert.Equal(new TimeSpan(21, 30, 7), payload.Hour);
+ Assert.Null(payload.NullableWithoutValue);
+ Assert.NotNull(payload.NullableWithValue);
+ Assert.Equal(new TimeSpan(21, 30, 7), payload.NullableWithValue.Value);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Custom_Formatted_Date()
+ {
+ var culture = CultureInfo.InvariantCulture;
+ var format = "dd yyyy MMM, hh:mm ss tt zzz";
+ var date = new DateTime(2010, 2, 8, 11, 11, 11);
+
+ var doc = new XDocument();
+
+ var root = new XElement("Person");
+ root.Add(new XElement("StartDate", date.ToString(format, culture)));
+
+ doc.Add(root);
+
+ var xml = new XmlAttributeDeserializer
+ {
+ DateFormat = format,
+ Culture = culture
+ };
+
+ var response = new RestResponse { Content = doc.ToString() };
+ var output = xml.Deserialize(response);
+
+ Assert.Equal(date, output.StartDate);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Elements_On_Default_Root()
+ {
+ var doc = CreateElementsXml();
+ var response = new RestResponse { Content = doc };
+
+ var d = new XmlAttributeDeserializer();
+ var p = d.Deserialize(response);
+
+ Assert.Equal("John Sheehan", p.Name);
+ Assert.Equal(new DateTime(2009, 9, 25, 0, 6, 1), p.StartDate);
+ Assert.Equal(28, p.Age);
+ Assert.Equal(long.MaxValue, p.BigNumber);
+ Assert.Equal(99.9999m, p.Percent);
+ Assert.Equal(false, p.IsCool);
+
+ Assert.Equal(new Guid(GuidString), p.UniqueId);
+ Assert.Equal(Guid.Empty, p.EmptyGuid);
+
+ Assert.Equal(new Uri("http://example.com", UriKind.RelativeOrAbsolute), p.Url);
+ Assert.Equal(new Uri("/foo/bar", UriKind.RelativeOrAbsolute), p.UrlPath);
+
+ Assert.Equal(Order.Third, p.Order);
+ Assert.Equal(Disposition.SoSo, p.Disposition);
+
+ Assert.NotNull(p.Friends);
+ Assert.Equal(10, p.Friends.Count);
+
+ Assert.NotNull(p.BestFriend);
+ Assert.Equal("The Fonz", p.BestFriend.Name);
+ Assert.Equal(1952, p.BestFriend.Since);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Attributes_On_Default_Root()
+ {
+ var doc = CreateAttributesXml();
+ var response = new RestResponse { Content = doc };
+
+ var d = new XmlAttributeDeserializer();
+ var p = d.Deserialize(response);
+
+ Assert.Equal("John Sheehan", p.Name);
+ Assert.Equal(new DateTime(2009, 9, 25, 0, 6, 1), p.StartDate);
+ Assert.Equal(28, p.Age);
+ Assert.Equal(long.MaxValue, p.BigNumber);
+ Assert.Equal(99.9999m, p.Percent);
+ Assert.Equal(false, p.IsCool);
+ Assert.Equal(new Guid(GuidString), p.UniqueId);
+ Assert.Equal(new Uri("http://example.com", UriKind.RelativeOrAbsolute), p.Url);
+ Assert.Equal(new Uri("/foo/bar", UriKind.RelativeOrAbsolute), p.UrlPath);
+
+ Assert.NotNull(p.BestFriend);
+ Assert.Equal("The Fonz", p.BestFriend.Name);
+ Assert.Equal(1952, p.BestFriend.Since);
+ }
+
+ [Fact]
+ public void Ignore_Protected_Property_That_Exists_In_Data()
+ {
+ var doc = CreateElementsXml();
+ var response = new RestResponse { Content = doc };
+
+ var d = new XmlAttributeDeserializer();
+ var p = d.Deserialize(response);
+
+ Assert.Null(p.IgnoreProxy);
+ }
+
+ [Fact]
+ public void Ignore_ReadOnly_Property_That_Exists_In_Data()
+ {
+ var doc = CreateElementsXml();
+ var response = new RestResponse { Content = doc };
+
+ var d = new XmlAttributeDeserializer();
+ var p = d.Deserialize(response);
+
+ Assert.Null(p.ReadOnlyProxy);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Names_With_Underscores_On_Default_Root()
+ {
+ var doc = CreateUnderscoresXml();
+ var response = new RestResponse { Content = doc };
+
+ var d = new XmlAttributeDeserializer();
+ var p = d.Deserialize(response);
+
+ Assert.Equal("John Sheehan", p.Name);
+ Assert.Equal(new DateTime(2009, 9, 25, 0, 6, 1), p.StartDate);
+ Assert.Equal(28, p.Age);
+ Assert.Equal(long.MaxValue, p.BigNumber);
+ Assert.Equal(99.9999m, p.Percent);
+ Assert.Equal(false, p.IsCool);
+ Assert.Equal(new Guid(GuidString), p.UniqueId);
+ Assert.Equal(new Uri("http://example.com", UriKind.RelativeOrAbsolute), p.Url);
+ Assert.Equal(new Uri("/foo/bar", UriKind.RelativeOrAbsolute), p.UrlPath);
+
+ Assert.NotNull(p.Friends);
+ Assert.Equal(10, p.Friends.Count);
+
+ Assert.NotNull(p.BestFriend);
+ Assert.Equal("The Fonz", p.BestFriend.Name);
+ Assert.Equal(1952, p.BestFriend.Since);
+
+ Assert.NotNull(p.Foes);
+ Assert.Equal(5, p.Foes.Count);
+ Assert.Equal("Yankees", p.Foes.Team);
+ }
+
+
+ [Fact]
+ public void Can_Deserialize_Names_With_Dashes_On_Default_Root()
+ {
+ var doc = CreateDashesXml();
+ var response = new RestResponse { Content = doc };
+
+ var d = new XmlAttributeDeserializer();
+ var p = d.Deserialize(response);
+
+ Assert.Equal("John Sheehan", p.Name);
+ Assert.Equal(new DateTime(2009, 9, 25, 0, 6, 1), p.StartDate);
+ Assert.Equal(28, p.Age);
+ Assert.Equal(long.MaxValue, p.BigNumber);
+ Assert.Equal(99.9999m, p.Percent);
+ Assert.Equal(false, p.IsCool);
+ Assert.Equal(new Guid(GuidString), p.UniqueId);
+ Assert.Equal(new Uri("http://example.com", UriKind.RelativeOrAbsolute), p.Url);
+ Assert.Equal(new Uri("/foo/bar", UriKind.RelativeOrAbsolute), p.UrlPath);
+
+ Assert.NotNull(p.Friends);
+ Assert.Equal(10, p.Friends.Count);
+
+ Assert.NotNull(p.BestFriend);
+ Assert.Equal("The Fonz", p.BestFriend.Name);
+ Assert.Equal(1952, p.BestFriend.Since);
+
+ Assert.NotNull(p.Foes);
+ Assert.Equal(5, p.Foes.Count);
+ Assert.Equal("Yankees", p.Foes.Team);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Names_With_Underscores_Without_Matching_Case_On_Default_Root ()
+ {
+ var doc = CreateLowercaseUnderscoresXml ();
+ var response = new RestResponse { Content = doc };
+
+ var d = new XmlAttributeDeserializer ();
+ var p = d.Deserialize (response);
+
+ Assert.Equal ("John Sheehan", p.Name);
+ Assert.Equal (new DateTime (2009, 9, 25, 0, 6, 1), p.StartDate);
+ Assert.Equal (28, p.Age);
+ Assert.Equal (long.MaxValue, p.BigNumber);
+ Assert.Equal (99.9999m, p.Percent);
+ Assert.Equal (false, p.IsCool);
+ Assert.Equal (new Guid (GuidString), p.UniqueId);
+ Assert.Equal (new Uri ("http://example.com", UriKind.RelativeOrAbsolute), p.Url);
+ Assert.Equal (new Uri ("/foo/bar", UriKind.RelativeOrAbsolute), p.UrlPath);
+
+ Assert.NotNull (p.Friends);
+ Assert.Equal (10, p.Friends.Count);
+
+ Assert.NotNull (p.BestFriend);
+ Assert.Equal ("The Fonz", p.BestFriend.Name);
+ Assert.Equal (1952, p.BestFriend.Since);
+
+ Assert.NotNull (p.Foes);
+ Assert.Equal (5, p.Foes.Count);
+ Assert.Equal ("Yankees", p.Foes.Team);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Lower_Cased_Root_Elements_With_Dashes()
+ {
+ var doc = CreateDashesXml();
+ var response = new RestResponse { Content = doc };
+
+ var d = new XmlAttributeDeserializer();
+ var p = d.Deserialize(response);
+
+ Assert.Equal("John Sheehan", p.Name);
+ Assert.Equal(new DateTime(2009, 9, 25, 0, 6, 1), p.StartDate);
+ Assert.Equal(28, p.Age);
+ Assert.Equal(long.MaxValue, p.BigNumber);
+ Assert.Equal(99.9999m, p.Percent);
+ Assert.Equal(false, p.IsCool);
+ Assert.Equal(new Guid(GuidString), p.UniqueId);
+ Assert.Equal(new Uri("http://example.com", UriKind.RelativeOrAbsolute), p.Url);
+ Assert.Equal(new Uri("/foo/bar", UriKind.RelativeOrAbsolute), p.UrlPath);
+
+ Assert.NotNull(p.Friends);
+ Assert.Equal(10, p.Friends.Count);
+
+ Assert.NotNull(p.BestFriend);
+ Assert.Equal("The Fonz", p.BestFriend.Name);
+ Assert.Equal(1952, p.BestFriend.Since);
+
+ Assert.NotNull(p.Foes);
+ Assert.Equal(5, p.Foes.Count);
+ Assert.Equal("Yankees", p.Foes.Team);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Root_Elements_Without_Matching_Case_And_Dashes()
+ {
+ var doc = CreateLowerCasedRootElementWithDashesXml();
+ var response = new RestResponse { Content = doc };
+
+ var d = new XmlAttributeDeserializer();
+ var p = d.Deserialize>(response);
+
+ Assert.NotNull(p);
+ Assert.Equal(1, p.Count);
+ Assert.Equal(45, p[0].ConceptId);
+ }
+
+
+ [Fact]
+ public void Can_Deserialize_Eventful_Xml()
+ {
+ var xmlpath = PathFor("eventful.xml");
+ var doc = XDocument.Load(xmlpath);
+ var response = new RestResponse { Content = doc.ToString() };
+
+ var d = new XmlAttributeDeserializer();
+ var output = d.Deserialize(response);
+
+ Assert.NotEmpty(output.venues);
+ Assert.Equal(3, output.venues.Count);
+ Assert.Equal("Tivoli", output.venues[0].name);
+ Assert.Equal("http://eventful.com/brisbane/venues/tivoli-/V0-001-002169294-8", output.venues[1].url);
+ Assert.Equal("V0-001-000266914-3", output.venues[2].id);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Lastfm_Xml()
+ {
+ var xmlpath = PathFor("Lastfm.xml");
+ var doc = XDocument.Load(xmlpath);
+ var response = new RestResponse { Content = doc.ToString() };
+
+ var d = new XmlAttributeDeserializer();
+ var output = d.Deserialize(response);
+
+ //Assert.NotEmpty(output.artists);
+ Assert.Equal("http://www.last.fm/event/328799+Philip+Glass+at+Barbican+Centre+on+12+June+2008", output.url);
+ Assert.Equal("http://www.last.fm/venue/8777860+Barbican+Centre", output.venue.url);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Google_Weather_Xml()
+ {
+ var xmlpath = PathFor("GoogleWeather.xml");
+ var doc = XDocument.Load(xmlpath);
+ var response = new RestResponse { Content = doc.ToString() };
+
+ var d = new XmlAttributeDeserializer();
+ var output = d.Deserialize(response);
+
+ Assert.NotEmpty(output.weather);
+ Assert.Equal(4, output.weather.Count);
+ Assert.Equal("Sunny", output.weather[0].condition.data);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Google_Weather_Xml_WithDeserializeAs()
+ {
+ var xmlpath = PathFor("GoogleWeather.xml");
+ var doc = XDocument.Load(xmlpath);
+ var response = new RestResponse { Content = doc.ToString() };
+
+ var d = new XmlAttributeDeserializer();
+ var output = d.Deserialize(response);
+
+ Assert.NotEmpty(output.Weather);
+ Assert.Equal(4, output.Weather.Count);
+ Assert.Equal("Sunny", output.Weather[0].Condition.Data);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Boolean_From_Number()
+ {
+ var xmlpath = PathFor("boolean_from_number.xml");
+ var doc = XDocument.Load(xmlpath);
+ var response = new RestResponse { Content = doc.ToString() };
+
+ var d = new XmlAttributeDeserializer();
+ var output = d.Deserialize(response);
+
+ Assert.True(output.Value);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Boolean_From_String()
+ {
+ var xmlpath = PathFor("boolean_from_string.xml");
+ var doc = XDocument.Load(xmlpath);
+ var response = new RestResponse { Content = doc.ToString() };
+
+ var d = new XmlAttributeDeserializer();
+ var output = d.Deserialize(response);
+
+ Assert.True(output.Value);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Empty_Elements_With_Attributes_to_Nullable_Values()
+ {
+ var doc = CreateXmlWithAttributesAndNullValues();
+
+ var xml = new XmlAttributeDeserializer();
+ var output = xml.Deserialize(new RestResponse {Content = doc});
+
+ Assert.Null(output.Id);
+ Assert.Null(output.StartDate);
+ Assert.Null(output.UniqueId);
+ }
+
+ [Fact]
+ public void Can_Deserialize_Mixture_Of_Empty_Elements_With_Attributes_And_Populated_Elements()
+ {
+ var doc = CreateXmlWithAttributesAndNullValuesAndPopulatedValues();
+
+ var xml = new XmlAttributeDeserializer();
+ var output = xml.Deserialize(new RestResponse {Content = doc});
+
+ Assert.Null(output.Id);
+ Assert.Null(output.StartDate);
+ Assert.Equal(new Guid(GuidString), output.UniqueId);
+ }
+
+ [Fact]
+ public void Can_Deserialize_DateTimeOffset()
+ {
+ var culture = CultureInfo.InvariantCulture;
+ var doc = new XDocument(culture);
+
+ DateTimeOffset DateTimeOffset = new DateTimeOffset(2013, 02, 08, 9, 18, 22, TimeSpan.FromHours(10));
+ DateTimeOffset? NullableDateTimeOffsetWithValue = new DateTimeOffset(2013, 02, 08, 9, 18, 23, TimeSpan.FromHours(10));
+
+ var root = new XElement("Dates");
+ root.Add(new XElement("DateTimeOffset", DateTimeOffset));
+ root.Add(new XElement("NullableDateTimeOffsetWithNull", string.Empty));
+ root.Add(new XElement("NullableDateTimeOffsetWithValue", NullableDateTimeOffsetWithValue));
+
+ doc.Add(root);
+
+ var xml = new XmlAttributeDeserializer
+ {
+ Culture = culture,
+ };
+
+ var response = new RestResponse { Content = doc.ToString() };
+
+ var d = new XmlAttributeDeserializer()
+ {
+ Culture = culture,
+ };
+ var payload = d.Deserialize(response);
+ Assert.Equal(DateTimeOffset, payload.DateTimeOffset);
+ Assert.Null(payload.NullableDateTimeOffsetWithNull);
+
+ Assert.True(payload.NullableDateTimeOffsetWithValue.HasValue);
+ Assert.Equal(NullableDateTimeOffsetWithValue, payload.NullableDateTimeOffsetWithValue);
+ }
+
+ private static string CreateUnderscoresXml()
+ {
+ var doc = new XDocument();
+ var root = new XElement("Person");
+ root.Add(new XElement("Name", "John Sheehan"));
+ root.Add(new XElement("Start_Date", new DateTime(2009, 9, 25, 0, 6, 1)));
+ root.Add(new XAttribute("Age", 28));
+ root.Add(new XElement("Percent", 99.9999m));
+ root.Add(new XElement("Big_Number", long.MaxValue));
+ root.Add(new XAttribute("Is_Cool", false));
+ root.Add(new XElement("Ignore", "dummy"));
+ root.Add(new XAttribute("Read_Only", "dummy"));
+ root.Add(new XElement("Unique_Id", new Guid(GuidString)));
+ root.Add(new XElement("Url", "http://example.com"));
+ root.Add(new XElement("Url_Path", "/foo/bar"));
+
+ root.Add(new XElement("Best_Friend",
+ new XElement("Name", "The Fonz"),
+ new XAttribute("Since", 1952)
+ ));
+
+ var friends = new XElement("Friends");
+ for (int i = 0; i < 10; i++)
+ {
+ friends.Add(new XElement("Friend",
+ new XElement("Name", "Friend" + i),
+ new XAttribute("Since", DateTime.Now.Year - i)
+ ));
+ }
+ root.Add(friends);
+
+ var foes = new XElement("Foes");
+ foes.Add(new XAttribute("Team", "Yankees"));
+ for (int i = 0; i < 5; i++)
+ {
+ foes.Add(new XElement("Foe", new XElement("Nickname", "Foe" + i)));
+ }
+ root.Add(foes);
+
+ doc.Add(root);
+ return doc.ToString();
+ }
+
+ private static string CreateLowercaseUnderscoresXml()
+ {
+ var doc = new XDocument();
+ var root = new XElement("Person");
+ root.Add(new XElement("Name", "John Sheehan"));
+ root.Add(new XElement("start_date", new DateTime(2009, 9, 25, 0, 6, 1)));
+ root.Add(new XAttribute("Age", 28));
+ root.Add(new XElement("Percent", 99.9999m));
+ root.Add(new XElement("big_number", long.MaxValue));
+ root.Add(new XAttribute("is_cool", false));
+ root.Add(new XElement("Ignore", "dummy"));
+ root.Add(new XAttribute("read_only", "dummy"));
+ root.Add(new XElement("unique_id", new Guid(GuidString)));
+ root.Add(new XElement("Url", "http://example.com"));
+ root.Add(new XElement("url_path", "/foo/bar"));
+
+ root.Add(new XElement("best_friend",
+ new XElement("name", "The Fonz"),
+ new XAttribute("Since", 1952)
+ ));
+
+ var friends = new XElement("Friends");
+ for (int i = 0; i < 10; i++)
+ {
+ friends.Add(new XElement("Friend",
+ new XElement("Name", "Friend" + i),
+ new XAttribute("Since", DateTime.Now.Year - i)
+ ));
+ }
+ root.Add(friends);
+
+ var foes = new XElement("Foes");
+ foes.Add(new XAttribute("Team", "Yankees"));
+ for (int i = 0; i < 5; i++)
+ {
+ foes.Add(new XElement("Foe", new XElement("Nickname", "Foe" + i)));
+ }
+ root.Add(foes);
+
+ doc.Add(root);
+
+ return doc.ToString();
+ }
+
+ private static string CreateDashesXml()
+ {
+ var doc = new XDocument();
+ var root = new XElement("Person");
+ root.Add(new XElement("Name", "John Sheehan"));
+ root.Add(new XElement("Start_Date", new DateTime(2009, 9, 25, 0, 6, 1)));
+ root.Add(new XAttribute("Age", 28));
+ root.Add(new XElement("Percent", 99.9999m));
+ root.Add(new XElement("Big-Number", long.MaxValue));
+ root.Add(new XAttribute("Is-Cool", false));
+ root.Add(new XElement("Ignore", "dummy"));
+ root.Add(new XAttribute("Read-Only", "dummy"));
+ root.Add(new XElement("Unique-Id", new Guid(GuidString)));
+ root.Add(new XElement("Url", "http://example.com"));
+ root.Add(new XElement("Url-Path", "/foo/bar"));
+
+ root.Add(new XElement("Best-Friend",
+ new XElement("Name", "The Fonz"),
+ new XAttribute("Since", 1952)
+ ));
+
+ var friends = new XElement("Friends");
+ for (int i = 0; i < 10; i++)
+ {
+ friends.Add(new XElement("Friend",
+ new XElement("Name", "Friend" + i),
+ new XAttribute("Since", DateTime.Now.Year - i)
+ ));
+ }
+ root.Add(friends);
+
+ var foes = new XElement("Foes");
+ foes.Add(new XAttribute("Team", "Yankees"));
+ for (int i = 0; i < 5; i++)
+ {
+ foes.Add(new XElement("Foe", new XElement("Nickname", "Foe" + i)));
+ }
+ root.Add(foes);
+
+ doc.Add(root);
+ return doc.ToString();
+ }
+
+ private static string CreateLowerCasedRootElementWithDashesXml()
+ {
+ var doc = new XDocument();
+ var root = new XElement("incoming-invoices",
+ new XElement("incoming-invoice",
+ new XElement("concept-id", 45)
+ )
+ );
+ doc.Add(root);
+ return doc.ToString();
+ }
+
+ private static string CreateElementsXml()
+ {
+ var doc = new XDocument();
+ var root = new XElement("Person");
+ root.Add(new XElement("Name", "John Sheehan"));
+ root.Add(new XElement("StartDate", new DateTime(2009, 9, 25, 0, 6, 1)));
+ root.Add(new XElement("Age", 28));
+ root.Add(new XElement("Percent", 99.9999m));
+ root.Add(new XElement("BigNumber", long.MaxValue));
+ root.Add(new XElement("IsCool", false));
+ root.Add(new XElement("Ignore", "dummy"));
+ root.Add(new XElement("ReadOnly", "dummy"));
+
+ root.Add(new XElement("UniqueId", new Guid(GuidString)));
+ root.Add(new XElement("EmptyGuid", ""));
+
+ root.Add(new XElement("Url", "http://example.com"));
+ root.Add(new XElement("UrlPath", "/foo/bar"));
+ root.Add(new XElement("Order", "third"));
+ root.Add(new XElement("Disposition", "so-so"));
+
+ root.Add(new XElement("BestFriend",
+ new XElement("Name", "The Fonz"),
+ new XElement("Since", 1952)
+ ));
+
+ var friends = new XElement("Friends");
+ for (int i = 0; i < 10; i++)
+ {
+ friends.Add(new XElement("Friend",
+ new XElement("Name", "Friend" + i),
+ new XElement("Since", DateTime.Now.Year - i)
+ ));
+ }
+ root.Add(friends);
+
+ doc.Add(root);
+ return doc.ToString();
+ }
+
+ private static string CreateAttributesXml()
+ {
+ var doc = new XDocument();
+ var root = new XElement("Person");
+ root.Add(new XAttribute("Name", "John Sheehan"));
+ root.Add(new XAttribute("StartDate", new DateTime(2009, 9, 25, 0, 6, 1)));
+ root.Add(new XAttribute("Age", 28));
+ root.Add(new XAttribute("Percent", 99.9999m));
+ root.Add(new XAttribute("BigNumber", long.MaxValue));
+ root.Add(new XAttribute("IsCool", false));
+ root.Add(new XAttribute("Ignore", "dummy"));
+ root.Add(new XAttribute("ReadOnly", "dummy"));
+ root.Add(new XAttribute("UniqueId", new Guid(GuidString)));
+ root.Add(new XAttribute("Url", "http://example.com"));
+ root.Add(new XAttribute("UrlPath", "/foo/bar"));
+
+ root.Add(new XElement("BestFriend",
+ new XAttribute("Name", "The Fonz"),
+ new XAttribute("Since", 1952)
+ ));
+
+ doc.Add(root);
+ return doc.ToString();
+ }
+
+ private static string CreateXmlWithNullValues()
+ {
+ var doc = new XDocument();
+ var root = new XElement("NullableValues");
+
+ root.Add(new XElement("Id", null),
+ new XElement("StartDate", null),
+ new XElement("UniqueId", null)
+ );
+
+ doc.Add(root);
+
+ return doc.ToString();
+ }
+
+ private static string CreateXmlWithoutEmptyValues(CultureInfo culture)
+ {
+ var doc = new XDocument();
+ var root = new XElement("NullableValues");
+
+ root.Add(new XElement("Id", 123),
+ new XElement("StartDate", new DateTime(2010, 2, 21, 9, 35, 00).ToString(culture)),
+ new XElement("UniqueId", new Guid(GuidString))
+ );
+
+ doc.Add(root);
+
+ return doc.ToString();
+ }
+
+ private static string CreateXmlWithEmptyNestedList()
+ {
+ var doc = new XDocument();
+ var root = new XElement("EmptyListSample");
+
+ root.Add(new XElement("Images"));
+
+ doc.Add(root);
+
+ return doc.ToString();
+ }
+
+ private static string CreateXmlWithEmptyInlineList()
+ {
+ var doc = new XDocument();
+ var root = new XElement("EmptyListSample");
+
+ doc.Add(root);
+
+ return doc.ToString();
+ }
+
+ private static string CreateXmlWithAttributesAndNullValues()
+ {
+ var doc = new XDocument();
+ var root = new XElement("NullableValues");
+
+ var idElement = new XElement("Id", null);
+ idElement.SetAttributeValue("SomeAttribute", "SomeAttribute_Value");
+ root.Add(idElement,
+ new XElement("StartDate", null),
+ new XElement("UniqueId", null)
+ );
+
+ doc.Add(root);
+
+ return doc.ToString();
+ }
+
+ private static string CreateXmlWithAttributesAndNullValuesAndPopulatedValues()
+ {
+ var doc = new XDocument();
+ var root = new XElement("NullableValues");
+
+ var idElement = new XElement("Id", null);
+ idElement.SetAttributeValue("SomeAttribute", "SomeAttribute_Value");
+ root.Add(idElement,
+ new XElement("StartDate", null),
+ new XElement("UniqueId", new Guid(GuidString))
+ );
+
+ doc.Add(root);
+
+ return doc.ToString();
+ }
+
+ }
+}
diff --git a/RestSharp.Tests/XmlTests.cs b/RestSharp.Tests/XmlDeserializerTests.cs
similarity index 96%
rename from RestSharp.Tests/XmlTests.cs
rename to RestSharp.Tests/XmlDeserializerTests.cs
index 29abaf5b7..6deef898d 100644
--- a/RestSharp.Tests/XmlTests.cs
+++ b/RestSharp.Tests/XmlDeserializerTests.cs
@@ -26,7 +26,7 @@
namespace RestSharp.Tests
{
- public class XmlTests
+ public class XmlDeserializerTests
{
private const string GuidString = "AC1FC4BC-087A-4242-B8EE-C53EBE9887A5";
private string SampleDataPath = Path.Combine(Environment.CurrentDirectory, "SampleData");
@@ -118,19 +118,6 @@ public void Can_Deserialize_Parentless_aka_Inline_List_Items_Without_Matching_Cl
Assert.Equal(4, output.Images.Count);
}
- [Fact]
- public void Can_Deserialize_Parentless_aka_Inline_List_Items_Without_Matching_Class_Name_Using_XmlAttributeDeserializer()
- {
- var xmlpath = PathFor("InlineListSample.xml");
- var doc = XDocument.Load(xmlpath);
-
- var xml = new XmlAttributeDeserializer();
- var output = xml.Deserialize(new RestResponse { Content = doc.ToString() });
-
- Assert.NotEmpty(output.Images);
- Assert.Equal(4, output.Images.Count);
- }
-
[Fact]
public void Can_Deserialize_Parentless_aka_Inline_List_Items_With_Matching_Class_Name()
{
@@ -144,19 +131,6 @@ public void Can_Deserialize_Parentless_aka_Inline_List_Items_With_Matching_Class
Assert.Equal(4, output.images.Count);
}
- [Fact]
- public void Can_Deserialize_Parentless_aka_Inline_List_Items_With_Matching_Class_Name_Using_XmlAttributeDeserializer()
- {
- var xmlpath = PathFor("InlineListSample.xml");
- var doc = XDocument.Load(xmlpath);
-
- var xml = new XmlAttributeDeserializer();
- var output = xml.Deserialize(new RestResponse { Content = doc.ToString() });
-
- Assert.NotEmpty(output.images);
- Assert.Equal(4, output.images.Count);
- }
-
[Fact]
public void Can_Deserialize_Parentless_aka_Inline_List_Items_With_Matching_Class_Name_With_Additional_Property()
{
@@ -274,11 +248,6 @@ public void Can_Deserialize_TimeSpan()
doc.Add(root);
- var xml = new XmlDeserializer
- {
- Culture = culture,
- };
-
var response = new RestResponse { Content = doc.ToString() };
var d = new XmlDeserializer()
diff --git a/RestSharp/Deserializers/XmlAttributeDeserializer.cs b/RestSharp/Deserializers/XmlAttributeDeserializer.cs
index 2cf8a7d6f..aea2af8ea 100644
--- a/RestSharp/Deserializers/XmlAttributeDeserializer.cs
+++ b/RestSharp/Deserializers/XmlAttributeDeserializer.cs
@@ -14,377 +14,36 @@
// limitations under the License.
#endregion
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Xml;
+using System.Reflection;
using System.Xml.Linq;
-using RestSharp.Authenticators.OAuth.Extensions;
using RestSharp.Extensions;
namespace RestSharp.Deserializers
{
- public class XmlAttributeDeserializer : IDeserializer
+ public class XmlAttributeDeserializer : XmlDeserializer
{
- public string RootElement { get; set; }
- public string Namespace { get; set; }
- public string DateFormat { get; set; }
- public CultureInfo Culture { get; set; }
-
- public XmlAttributeDeserializer()
- {
- Culture = CultureInfo.InvariantCulture;
- }
-
- public T Deserialize(IRestResponse response)
- {
- if (response.Content == null)
- return default(T);
-
- var doc = XDocument.Parse(response.Content);
- var root = doc.Root;
- if (RootElement.HasValue() && doc.Root != null)
- {
- root = doc.Root.Element(RootElement.AsNamespaced(Namespace));
- }
-
- // autodetect xml namespace
- if (!Namespace.HasValue())
- {
- RemoveNamespace(doc);
- }
-
- var x = Activator.CreateInstance();
- var objType = x.GetType();
-
- if (objType.IsSubclassOfRawGeneric(typeof(List<>)))
- {
- x = (T)HandleListDerivative(x, root, objType.Name, objType);
- }
- else
- {
- Map(x, root);
- }
-
- return x;
- }
-
- void RemoveNamespace(XDocument xdoc)
- {
- foreach (XElement e in xdoc.Root.DescendantsAndSelf())
- {
- if (e.Name.Namespace != XNamespace.None)
- {
- e.Name = XNamespace.None.GetName(e.Name.LocalName);
- }
- if (e.Attributes().Any(a => a.IsNamespaceDeclaration || a.Name.Namespace != XNamespace.None))
- {
- e.ReplaceAttributes(e.Attributes().Select(a => a.IsNamespaceDeclaration ? null : a.Name.Namespace != XNamespace.None ? new XAttribute(XNamespace.None.GetName(a.Name.LocalName), a.Value) : a));
- }
- }
- }
-
- private void Map(object x, XElement root)
- {
- var objType = x.GetType();
- var props = objType.GetProperties();
-
- foreach (var prop in props)
- {
- var type = prop.PropertyType;
-
- if (!type.IsPublic || !prop.CanWrite)
- continue;
-
- var name = prop.Name.AsNamespaced(Namespace);
- var isAttribute = false;
- //Check for the DeserializeAs attribute on the property
- var options = prop.GetAttribute();
- if (options != null)
- {
- name = options.Name ?? name;
- isAttribute = options.Attribute;
- }
-
- var value = GetValueFromXml(root, name, isAttribute);
-
- var stringValue = value as string;
- if (stringValue.IsNullOrBlank())
- {
- // special case for inline list items
- if (type.IsGenericType)
- {
- var genericType = type.GetGenericArguments()[0];
-
- var first = GetElementByName(root, genericType.Name);
- if (first != null)
- {
- var elements = root.Elements(first.Name);
-
- var list = (IList)Activator.CreateInstance(type);
- PopulateListFromElements(genericType, elements, list);
- prop.SetValue(x, list, null);
-
- }
- }
- continue;
- }
-
- // check for nullable and extract underlying type
- if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
- {
- type = type.GetGenericArguments()[0];
-
- if (string.IsNullOrEmpty(value.ToString()))
- {
- continue;
- }
- }
-
- if (type == typeof(bool))
- {
- var toConvert = value.ToString().ToLower();
- prop.SetValue(x, XmlConvert.ToBoolean(toConvert), null);
- }
- else if (type.IsPrimitive)
- {
- prop.SetValue(x, value.ChangeType(type, Culture), null);
- }
- else if (type.IsEnum)
- {
- var converted = type.FindEnumValue(value.ToString(), Culture);
- prop.SetValue(x, converted, null);
- }
- else if (type == typeof(Uri))
- {
- var uri = new Uri(value.ToString(), UriKind.RelativeOrAbsolute);
- prop.SetValue(x, uri, null);
- }
- else if (type == typeof(string))
- {
- prop.SetValue(x, value, null);
- }
- else if (type == typeof(DateTime))
- {
- if (DateFormat.HasValue())
- {
- value = DateTime.ParseExact(value.ToString(), DateFormat, Culture);
- }
- else
- {
- value = DateTime.Parse(value.ToString(), Culture);
- }
-
- prop.SetValue(x, value, null);
- }
- else if (type == typeof(Decimal))
- {
- value = Decimal.Parse(value.ToString(), Culture);
- prop.SetValue(x, value, null);
- }
- else if (type == typeof(Guid))
- {
- value = new Guid(value.ToString());
- prop.SetValue(x, value, null);
- }
- else if (type.IsGenericType)
- {
- var t = type.GetGenericArguments()[0];
- var list = (IList)Activator.CreateInstance(type);
-
- var container = GetElementByName(root, name);
- var first = container.Elements().FirstOrDefault();
-
- var elements = container.Elements().Where(d => d.Name == first.Name);
- PopulateListFromElements(t, elements, list);
-
- prop.SetValue(x, list, null);
- }
- else if (type.IsSubclassOfRawGeneric(typeof(List<>)))
- {
- // handles classes that derive from List
- // e.g. a collection that also has properties
- var list = HandleListDerivative(x, root, name.ToString(), type);
- prop.SetValue(x, list, null);
- }
- else
- {
- // nested property classes
- if (root != null)
- {
- var element = GetElementByName(root, name);
- if (element != null)
- {
- var item = CreateAndMap(type, element);
- prop.SetValue(x, item, null);
- }
- }
- }
- }
- }
-
- private void PopulateListFromElements(Type t, IEnumerable elements, IList list)
- {
- foreach (var element in elements)
- {
- var item = CreateAndMap(t, element);
- list.Add(item);
- }
- }
-
- private object HandleListDerivative(object x, XElement root, string propName, Type type)
- {
- var t = type.BaseType.GetGenericArguments()[0];
-
- var name = t.Name;
- //Gets the DeserialiseAs Attribute for the Class that the list uses
- var options = t.GetAttribute();
- if (options != null)
- {
- name = options.Name ?? name;
- }
-
- var lowerName = name.ToLower();
- var camelName = name.ToCamelCase(Culture);
-
- var list = (IList)Activator.CreateInstance(type);
-
- IEnumerable elements = null;
-
- if (root.Descendants(name.AsNamespaced(Namespace)).Count() != 0)
- {
- elements = root.Descendants(t.Name.AsNamespaced(Namespace));
- }
-
- if (root.Descendants(lowerName).Count() != 0)
- {
- elements = root.Descendants(lowerName);
- }
-
- if (root.Descendants(camelName).Count() != 0)
- {
- elements = root.Descendants(camelName);
- }
-
- PopulateListFromElements(t, elements, list);
-
- // get properties too, not just list items
- Map(list, root.Element(propName.AsNamespaced(Namespace)));
-
- return list;
- }
-
- private object CreateAndMap(Type t, XElement element)
- {
- var item = Activator.CreateInstance(t);
- Map(item, element);
- return item;
- }
-
- private object GetValueFromXml(XElement root, XName name, bool attribute)
- {
- object val = null;
-
- if (root == null) return null;
-
- //check if the property is set as an Attribute using DeserializeAs
- if (attribute)
- {
- var attributeVal = GetAttributeByName(root, name);
- if (attributeVal != null)
- {
- val = attributeVal.Value;
- }
- }
- else
- {
- //Not set as an attribute
- var element = GetElementByName(root, name);
- if (element == null)
- {
- var attributeVal = GetAttributeByName(root, name);
- if (attributeVal != null)
- {
- val = attributeVal.Value;
- }
- }
- else
- {
- if (!element.IsEmpty || element.HasElements || element.HasAttributes)
- {
- val = element.Value;
- }
- }
- }
-
- return val;
- }
-
- private XElement GetElementByName(XElement root, XName name)
- {
- var lowerName = XName.Get(name.LocalName.ToLower(), name.NamespaceName);
- var camelName = XName.Get(name.LocalName.ToCamelCase(Culture), name.NamespaceName);
-
- if (root.Element(name) != null)
- {
- return root.Element(name);
- }
-
- if (root.Element(lowerName) != null)
- {
- return root.Element(lowerName);
- }
-
- if (root.Element(camelName) != null)
- {
- return root.Element(camelName);
- }
-
- if (name == "Value" && root.Value != null)
- {
- return root;
- }
-
- // try looking for element that matches sanitized property name (Order by depth)
- var element = root.Descendants().OrderBy(d => d.Ancestors().Count()).FirstOrDefault(d => d.Name.LocalName.RemoveUnderscoresAndDashes() == name.LocalName);
- if (element != null)
- {
- return element;
- }
-
- return null;
- }
-
- private XAttribute GetAttributeByName(XElement root, XName name)
- {
- var lowerName = XName.Get(name.LocalName.ToLower(), name.NamespaceName);
- var camelName = XName.Get(name.LocalName.ToCamelCase(Culture), name.NamespaceName);
-
- if (root.Attribute(name) != null)
- {
- return root.Attribute(name);
- }
-
- if (root.Attribute(lowerName) != null)
- {
- return root.Attribute(lowerName);
- }
-
- if (root.Attribute(camelName) != null)
- {
- return root.Attribute(camelName);
- }
-
- // try looking for element that matches sanitized property name
- var element = root.Attributes().FirstOrDefault(d => d.Name.LocalName.RemoveUnderscoresAndDashes() == name.LocalName);
- if (element != null)
- {
- return element;
- }
-
- return null;
- }
+ protected override object GetValueFromXml(XElement root, XName name, PropertyInfo prop)
+ {
+ var isAttribute = false;
+
+ //Check for the DeserializeAs attribute on the property
+ var options = prop.GetAttribute();
+ if (options != null)
+ {
+ name = options.Name ?? name;
+ isAttribute = options.Attribute;
+ }
+
+ if (isAttribute)
+ {
+ var attributeVal = GetAttributeByName(root, name);
+ if (attributeVal != null)
+ {
+ return attributeVal.Value;
+ }
+ }
+
+ return base.GetValueFromXml(root, name, prop);
+ }
}
}
diff --git a/RestSharp/Deserializers/XmlDeserializer.cs b/RestSharp/Deserializers/XmlDeserializer.cs
index 0d7200766..3d7e93cd0 100644
--- a/RestSharp/Deserializers/XmlDeserializer.cs
+++ b/RestSharp/Deserializers/XmlDeserializer.cs
@@ -18,6 +18,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
using System.Xml.Linq;
using RestSharp.Extensions;
@@ -39,7 +40,7 @@ public XmlDeserializer()
Culture = CultureInfo.InvariantCulture;
}
- public T Deserialize(IRestResponse response)
+ public virtual T Deserialize(IRestResponse response)
{
if (string.IsNullOrEmpty( response.Content ))
return default(T);
@@ -72,7 +73,7 @@ public T Deserialize(IRestResponse response)
return x;
}
- void RemoveNamespace(XDocument xdoc)
+ private void RemoveNamespace(XDocument xdoc)
{
foreach (XElement e in xdoc.Root.DescendantsAndSelf())
{
@@ -87,7 +88,7 @@ void RemoveNamespace(XDocument xdoc)
}
}
- private void Map(object x, XElement root)
+ protected virtual void Map(object x, XElement root)
{
var objType = x.GetType();
var props = objType.GetProperties();
@@ -100,7 +101,7 @@ private void Map(object x, XElement root)
continue;
var name = prop.Name.AsNamespaced(Namespace);
- var value = GetValueFromXml(root, name);
+ var value = GetValueFromXml(root, name, prop);
if (value == null)
{
@@ -342,7 +343,7 @@ private object HandleListDerivative(object x, XElement root, string propName, Ty
return list;
}
- private object CreateAndMap(Type t, XElement element)
+ protected virtual object CreateAndMap(Type t, XElement element)
{
object item;
if (t == typeof(String))
@@ -362,7 +363,7 @@ private object CreateAndMap(Type t, XElement element)
return item;
}
- private object GetValueFromXml(XElement root, XName name)
+ protected virtual object GetValueFromXml(XElement root, XName name, PropertyInfo prop)
{
object val = null;
@@ -389,7 +390,7 @@ private object GetValueFromXml(XElement root, XName name)
return val;
}
- private XElement GetElementByName(XElement root, XName name)
+ protected virtual XElement GetElementByName(XElement root, XName name)
{
var lowerName = name.LocalName.ToLower().AsNamespaced(name.NamespaceName);
var camelName = name.LocalName.ToCamelCase(Culture).AsNamespaced(name.NamespaceName);
@@ -430,7 +431,7 @@ private XElement GetElementByName(XElement root, XName name)
return null;
}
- private XAttribute GetAttributeByName(XElement root, XName name)
+ protected virtual XAttribute GetAttributeByName(XElement root, XName name)
{
var lowerName = name.LocalName.ToLower().AsNamespaced(name.NamespaceName);
var camelName = name.LocalName.ToCamelCase(Culture).AsNamespaced(name.NamespaceName);