diff --git a/nanoFramework.Json.Test/JsonSerializerOptionsTests.cs b/nanoFramework.Json.Test/JsonSerializerOptionsTests.cs
index bf7f70a..40ccf7a 100644
--- a/nanoFramework.Json.Test/JsonSerializerOptionsTests.cs
+++ b/nanoFramework.Json.Test/JsonSerializerOptionsTests.cs
@@ -119,6 +119,32 @@ public void Can_serialize_and_deserialize_arrays_of_class_objects()
OutputHelper.WriteLine("");
}
+ [TestMethod]
+ public void Can_deserialize_string_starting_with_unicode_escape()
+ {
+
+ OutputHelper.WriteLine("Starting test...");
+
+ Console.WriteLine($"{TimeSpan.FromHours(1).TotalMilliseconds}");
+
+ JsonTestCompany test = new JsonTestCompany
+ {
+ CompanyID = 10,
+ CompanyName = "5 Guys"
+ };
+
+ var jsonString = "{\u0022CompanyID\u0022:10,\u0022CompanyName\u0022:\u00225 Guys\u0022}";
+
+
+ JsonTestCompany deserializedResult = (JsonTestCompany)JsonConvert.DeserializeObject(jsonString, typeof(JsonTestCompany));
+
+ Assert.NotNull(deserializedResult);
+ Assert.Equal("5 Guys", deserializedResult.CompanyName);
+ Assert.Equal(10, deserializedResult.CompanyID);
+
+ OutputHelper.WriteLine("Finished test...");
+ }
+
[TestMethod]
public void Can_serialize_and_deserialize_arrays_of_class_objects_when_array_items_may_be_null()
{
diff --git a/nanoFramework.Json/JsonConvert.cs b/nanoFramework.Json/JsonConvert.cs
index 34722f6..25c1294 100644
--- a/nanoFramework.Json/JsonConvert.cs
+++ b/nanoFramework.Json/JsonConvert.cs
@@ -33,7 +33,10 @@ private struct LexToken
///
/// Convert an object to a JSON string.
///
- /// The value to convert. Supported types are: Boolean, String, Byte, (U)Int16, (U)Int32, Float, Double, Decimal, Array, IDictionary, IEnumerable, Guid, Datetime, DictionaryEntry, Object and null.
+ ///
+ /// The value to convert. Supported types are: Boolean, String, Byte, (U)Int16, (U)Int32,
+ /// Float, Double, Decimal, Array, IDictionary, IEnumerable, Guid, Datetime, DictionaryEntry, Object and null.
+ ///
/// The JSON object as a string or null when the value type is not supported.
/// For objects, only public properties with getters are converted.
public static string SerializeObject(object oSource)
@@ -1088,7 +1091,7 @@ private static LexToken GetNextTokenInternal(ref int jsonPos, ref byte[] jsonByt
return EndToken(sb);
}
- //TODO: replace with a mapping array? This switch is really incomplete.
+ // handle escaped characters
switch (ch)
{
case 't':
@@ -1115,15 +1118,21 @@ private static LexToken GetNextTokenInternal(ref int jsonPos, ref byte[] jsonByt
ch = '\\';
break;
- case 'u':
- unicodeEncoded = true;
- break;
-
case '"':
ch = '"';
break;
+ case '\'':
+ ch = '\'';
+ break;
+
+ case 'u':
+ // unicode escape \uXXXX - we'll handle below
+ unicodeEncoded = true;
+ break;
+
default:
+ // unknown escape sequence
throw new DeserializationException();
}
}
@@ -1132,56 +1141,32 @@ private static LexToken GetNextTokenInternal(ref int jsonPos, ref byte[] jsonByt
{
if (unicodeEncoded)
{
- int numberCounter = 0;
-
- // next 4 chars have to be numeric
- StringBuilder encodedValue = new();
-
- // advance position to next char
- jsonPos++;
- ch = (char)jsonBytes[jsonPos];
-
- for (int i = 0; i < 4; i++)
+ // We must decode exactly 4 hex digits following the '\u'
+ // Ensure there are at least 4 bytes remaining
+ if (jsonPos + 4 > jsonBytes.Length)
{
- if (IsNumberChar(ch))
- {
- numberCounter++;
-
- encodedValue.Append(ch);
-
- ch = (char)jsonBytes[jsonPos];
-
- if (IsNumberChar(ch))
- {
- // We're still working on the number - advance jsonPos
- jsonPos++;
- }
- }
+ throw new DeserializationException();
}
- if (numberCounter == 4)
+ // Read 4 ASCII hex chars (they should be ASCII hex digits)
+ int val = 0;
+ for (int i = 0; i < 4; i++)
{
- // we're good with the encoded data
- // try parse number as an UTF-8 char
- try
- {
- // NOTE: the encoded value has hexadecimal format
- int unicodeChar = Convert.ToInt16(encodedValue.ToString(), 16);
-
- _ = sb.Append((char)unicodeChar);
- }
- catch
+ char hc = (char)jsonBytes[jsonPos++];
+ int digit;
+ if (hc >= '0' && hc <= '9') digit = hc - '0';
+ else if (hc >= 'a' && hc <= 'f') digit = 10 + (hc - 'a');
+ else if (hc >= 'A' && hc <= 'F') digit = 10 + (hc - 'A');
+ else
{
- // couldn't parse this number as a valid Unicode value
throw new DeserializationException();
}
+
+ val = (val << 4) + digit;
}
- else
- {
- // anything else, we can't parse it properly
- // throw exception
- throw new DeserializationException();
- }
+
+ // Append the decoded unicode character
+ sb.Append((char)val);
}
else
{