diff --git a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs index 25fbcf87..031f0ec3 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs @@ -701,6 +701,22 @@ namespace Newtonsoft.Json.Tests.Linq } #endif + [Test] + public void ToStringUri() + { + JArray a = new JArray( + new JValue(new Uri("http://james.newtonking.com")), + new JValue(new Uri("http://james.newtonking.com/install?v=7.0.1")) + ); + + Console.WriteLine(a.ToString()); + + StringAssert.AreEqual(@"[ + ""http://james.newtonking.com"", + ""http://james.newtonking.com/install?v=7.0.1"" +]", a.ToString()); + } + #if !NET20 [Test] public void ParseIsoTimeZones() diff --git a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs index cd2b1f3d..79a3b5d2 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs @@ -25,6 +25,7 @@ using System; using System.Collections.Generic; +using System.Text; #if !(PORTABLE || PORTABLE40 || NET35 || NET20) using System.Numerics; #endif @@ -739,6 +740,55 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath Assert.AreEqual(1L, v.Value); } + [Test] + public void QueryAgainstNonStringValues() + { + IList values = new List + { + "ff2dc672-6e15-4aa2-afb0-18f4f69596ad", + Guid.Parse("ff2dc672-6e15-4aa2-afb0-18f4f69596ad"), + "http://localhost", + new Uri("http://localhost"), + "2000-12-05T05:07:59Z", + new DateTime(2000, 12, 5, 5, 7, 59, DateTimeKind.Utc), +#if !NET20 + "2000-12-05T05:07:59-10:00", + new DateTimeOffset(2000, 12, 5, 5, 7, 59, -TimeSpan.FromHours(10)), +#endif + "SGVsbG8gd29ybGQ=", + Encoding.UTF8.GetBytes("Hello world"), + "365.23:59:59", + new TimeSpan(365, 23, 59, 59) + }; + + JObject o = new JObject( + new JProperty("prop", + new JArray( + values.Select(v => new JObject(new JProperty("childProp", v))) + ) + ) + ); + + IList t = o.SelectTokens("$.prop[?(@.childProp =='ff2dc672-6e15-4aa2-afb0-18f4f69596ad')]").ToList(); + Assert.AreEqual(2, t.Count); + + t = o.SelectTokens("$.prop[?(@.childProp =='http://localhost')]").ToList(); + Assert.AreEqual(2, t.Count); + + t = o.SelectTokens("$.prop[?(@.childProp =='2000-12-05T05:07:59Z')]").ToList(); + Assert.AreEqual(2, t.Count); + +#if !NET20 + t = o.SelectTokens("$.prop[?(@.childProp =='2000-12-05T05:07:59-10:00')]").ToList(); + Assert.AreEqual(2, t.Count); +#endif + + t = o.SelectTokens("$.prop[?(@.childProp =='SGVsbG8gd29ybGQ=')]").ToList(); + Assert.AreEqual(2, t.Count); + + t = o.SelectTokens("$.prop[?(@.childProp =='365.23:59:59')]").ToList(); + Assert.AreEqual(2, t.Count); + } [Test] public void Example() diff --git a/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs b/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs index 8e5adb77..7372da0c 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs @@ -1235,7 +1235,7 @@ keyword such as type of business."" ""NullableGuid"": ""9e9f3adf-e017-4f72-91e0-617ebe85967d"", ""TimeSpan"": ""1.00:00:00"", ""NullableTimeSpan"": ""01:00:00"", - ""Uri"": ""http://testuri.com/"" + ""Uri"": ""http://testuri.com"" }", o.ToString()); UriGuidTimeSpanTestClass c2 = o.ToObject(); @@ -1244,6 +1244,10 @@ keyword such as type of business."" Assert.AreEqual(c1.TimeSpan, c2.TimeSpan); Assert.AreEqual(c1.NullableTimeSpan, c2.NullableTimeSpan); Assert.AreEqual(c1.Uri, c2.Uri); + + string j = JsonConvert.SerializeObject(c1, Formatting.Indented); + + StringAssert.AreEqual(j, o.ToString()); } [Test] diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index 048e4c42..8e2cf36b 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -717,10 +717,12 @@ namespace Newtonsoft.Json.Linq writer.WriteValue((byte[])_value); return; case JTokenType.Guid: - case JTokenType.Uri: case JTokenType.TimeSpan: writer.WriteValue((_value != null) ? _value.ToString() : null); return; + case JTokenType.Uri: + writer.WriteValue((_value != null) ? ((Uri) _value).OriginalString : null); + return; } throw MiscellaneousUtils.CreateArgumentOutOfRangeException("TokenType", _valueType, "Unexpected token type."); diff --git a/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs b/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs index c07f3a08..9e4c9e3c 100644 --- a/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs +++ b/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs @@ -1,5 +1,8 @@ using System; using System.Collections.Generic; +using System.Globalization; +using System.IO; +using Newtonsoft.Json.Utilities; namespace Newtonsoft.Json.Linq.JsonPath { @@ -72,11 +75,11 @@ namespace Newtonsoft.Json.Linq.JsonPath switch (Operator) { case QueryOperator.Equals: - if (v != null && v.Equals(Value)) + if (v != null && EqualsWithStringCoercion(v, Value)) return true; break; case QueryOperator.NotEquals: - if (v != null && !v.Equals(Value)) + if (v != null && !EqualsWithStringCoercion(v, Value)) return true; break; case QueryOperator.GreaterThan: @@ -104,5 +107,58 @@ namespace Newtonsoft.Json.Linq.JsonPath return false; } + + private bool EqualsWithStringCoercion(JValue value, JValue queryValue) + { + if (value.Equals(queryValue)) + { + return true; + } + + if (queryValue.Type != JTokenType.String) + { + return false; + } + + string queryValueString = (string)queryValue.Value; + + string currentValueString; + + // potential performance issue with converting every value to string? + switch (value.Type) + { + case JTokenType.Date: + using (StringWriter writer = StringUtils.CreateStringWriter(64)) + { +#if !NET20 + if (value.Value is DateTimeOffset) + { + DateTimeUtils.WriteDateTimeOffsetString(writer, (DateTimeOffset)value.Value, DateFormatHandling.IsoDateFormat, null, CultureInfo.InvariantCulture); + } + else +#endif + { + DateTimeUtils.WriteDateTimeString(writer, (DateTime)value.Value, DateFormatHandling.IsoDateFormat, null, CultureInfo.InvariantCulture); + } + + currentValueString = writer.ToString(); + } + break; + case JTokenType.Bytes: + currentValueString = Convert.ToBase64String((byte[])value.Value); + break; + case JTokenType.Guid: + case JTokenType.TimeSpan: + currentValueString = value.Value.ToString(); + break; + case JTokenType.Uri: + currentValueString = ((Uri)value.Value).OriginalString; + break; + default: + return false; + } + + return string.Equals(currentValueString, queryValueString, StringComparison.Ordinal); + } } } \ No newline at end of file