diff --git a/Src/Newtonsoft.Json.Tests/Issues/Issue1592.cs b/Src/Newtonsoft.Json.Tests/Issues/Issue1592.cs new file mode 100644 index 00000000..3aa5b4bf --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Issues/Issue1592.cs @@ -0,0 +1,65 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +#if DNXCORE50 +using Xunit; +using Test = Xunit.FactAttribute; +using Assert = Newtonsoft.Json.Tests.XUnitAssert; +#else +using NUnit.Framework; +#endif + +namespace Newtonsoft.Json.Tests.Issues +{ + [TestFixture] + public class Issue1592 : TestFixtureBase + { + [Test] + public void Test() + { + string json = @"{ +""test customer's"": ""testing"" +}"; + + StringReader stringReader = new StringReader(json); + JsonTextReader reader = new JsonTextReader(stringReader); + + reader.Read(); + reader.Read(); + reader.Read(); + + Assert.AreEqual("testing", reader.Value); + Assert.AreEqual("['test customer\\'s']", reader.Path); + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs index 887b5453..d72dc8d5 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs @@ -34,6 +34,7 @@ using Newtonsoft.Json.Converters; using Xunit; using Test = Xunit.FactAttribute; using Assert = Newtonsoft.Json.Tests.XUnitAssert; +using TestCase = Xunit.InlineDataAttribute; #else using NUnit.Framework; #endif @@ -1290,5 +1291,37 @@ namespace Newtonsoft.Json.Tests.Linq ExceptionAssert.Throws(() => JToken.Parse(json), "Additional text encountered after finished reading JSON content: {. Path '', line 3, position 0."); } + +#if DNXCORE50 + [Theory] +#endif + [TestCase("test customer", "['test customer']")] + [TestCase("test customer's", "['test customer\\'s']")] + [TestCase("testcustomer's", "['testcustomer\\'s']")] + [TestCase("testcustomer", "testcustomer")] + [TestCase("test.customer", "['test.customer']")] + [TestCase("test\rcustomer", "['test\\rcustomer']")] + [TestCase("test\ncustomer", "['test\\ncustomer']")] + [TestCase("test\tcustomer", "['test\\tcustomer']")] + [TestCase("test\bcustomer", "['test\\bcustomer']")] + [TestCase("test\fcustomer", "['test\\fcustomer']")] + [TestCase("test/customer", "['test/customer']")] + [TestCase("test\\customer", "['test\\\\customer']")] + [TestCase("\"test\"customer", "['\"test\"customer']")] + public void PathEscapingTest(string name, string expectedPath) + { + JValue v = new JValue("12345"); + JObject o = new JObject + { + [name] = v + }; + + string path = v.Path; + + Assert.AreEqual(expectedPath, path); + + JToken token = o.SelectToken(path); + Assert.AreEqual(v, token); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonPosition.cs b/Src/Newtonsoft.Json/JsonPosition.cs index bbdfef90..b301993e 100644 --- a/Src/Newtonsoft.Json/JsonPosition.cs +++ b/Src/Newtonsoft.Json/JsonPosition.cs @@ -26,6 +26,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Text; using Newtonsoft.Json.Utilities; @@ -41,7 +42,7 @@ namespace Newtonsoft.Json internal struct JsonPosition { - private static readonly char[] SpecialCharacters = { '.', ' ', '[', ']', '(', ')' }; + private static readonly char[] SpecialCharacters = { '.', ' ', '\'', '/', '"', '[', ']', '(', ')', '\t', '\n', '\r', '\f', '\b', '\\', '\u0085', '\u2028', '\u2029' }; internal JsonContainerType Type; internal int Position; @@ -70,7 +71,7 @@ namespace Newtonsoft.Json } } - internal void WriteTo(StringBuilder sb) + internal void WriteTo(StringBuilder sb, ref StringWriter writer, ref char[] buffer) { switch (Type) { @@ -79,7 +80,14 @@ namespace Newtonsoft.Json if (propertyName.IndexOfAny(SpecialCharacters) != -1) { sb.Append(@"['"); - sb.Append(propertyName); + + if (writer == null) + { + writer = new StringWriter(sb); + } + + JavaScriptUtils.WriteEscapedJavaScriptString(writer, propertyName, '\'', false, JavaScriptUtils.SingleQuoteCharEscapeFlags, StringEscapeHandling.Default, null, ref buffer); + sb.Append(@"']"); } else @@ -122,16 +130,18 @@ namespace Newtonsoft.Json } StringBuilder sb = new StringBuilder(capacity); + StringWriter writer = null; + char[] buffer = null; if (positions != null) { foreach (JsonPosition state in positions) { - state.WriteTo(sb); + state.WriteTo(sb, ref writer, ref buffer); } } if (currentPosition != null) { - currentPosition.GetValueOrDefault().WriteTo(sb); + currentPosition.GetValueOrDefault().WriteTo(sb, ref writer, ref buffer); } return sb.ToString(); diff --git a/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs b/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs index 27986dc2..331e6f6a 100644 --- a/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs +++ b/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs @@ -661,28 +661,44 @@ namespace Newtonsoft.Json.Linq.JsonPath if (currentChar == '\\' && _currentIndex + 1 < _expression.Length) { _currentIndex++; + currentChar = _expression[_currentIndex]; - if (_expression[_currentIndex] == '\'') + char resolvedChar; + switch (currentChar) { - sb.Append('\''); - } - else if (_expression[_currentIndex] == '\\') - { - sb.Append('\\'); - } - else - { - throw new JsonException(@"Unknown escape character: \" + _expression[_currentIndex]); + case 'b': + resolvedChar = '\b'; + break; + case 't': + resolvedChar = '\t'; + break; + case 'n': + resolvedChar = '\n'; + break; + case 'f': + resolvedChar = '\f'; + break; + case 'r': + resolvedChar = '\r'; + break; + case '\\': + case '"': + case '\'': + case '/': + resolvedChar = currentChar; + break; + default: + throw new JsonException(@"Unknown escape character: \" + _expression[_currentIndex]); } + sb.Append(resolvedChar); + _currentIndex++; } else if (currentChar == '\'') { _currentIndex++; - { - return sb.ToString(); - } + return sb.ToString(); } else {