Pārlūkot izejas kodu

Added support for using register parsers to parse dictionary keys and parser for Guid.

Brad Robinson 3 gadi atpakaļ
vecāks
revīzija
023894240c
3 mainītis faili ar 172 papildinājumiem un 108 dzēšanām
  1. 30 0
      Topten.JsonKit.Test/TestGuid.cs
  2. 112 92
      Topten.JsonKit/IJsonReader.cs
  3. 30 16
      Topten.JsonKit/JsonReader.cs

+ 30 - 0
Topten.JsonKit.Test/TestGuid.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Topten.JsonKit;
+using System.IO;
+using System.Reflection;
+using Xunit;
+
+namespace TestCases
+{
+    public class TestGuid
+    {
+        [Fact]
+        public void Test()
+        {
+            var src = new Dictionary<Guid, string>()
+            {
+                { Guid.NewGuid(), "First" },
+                { Guid.NewGuid(), "Second" },
+                { Guid.NewGuid(), "Third" },
+            };
+
+            var json = Json.Format(src);
+
+            var dest = Json.Parse<Dictionary<Guid, string>>(json);
+
+        }
+    }
+}

+ 112 - 92
Topten.JsonKit/IJsonReader.cs

@@ -1,96 +1,96 @@
-// JsonKit v0.5 - A simple but flexible Json library in a single .cs file.
-// 
-// Copyright (C) 2014 Topten Software (contact@toptensoftware.com) All rights reserved.
-// 
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product 
-// 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.
-
-using System;
+// JsonKit v0.5 - A simple but flexible Json library in a single .cs file.
+// 
+// Copyright (C) 2014 Topten Software (contact@toptensoftware.com) All rights reserved.
+// 
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product 
+// 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.
+
+using System;
 using System.Globalization;
 using System.IO;
-using System.Reflection;
-
-
-namespace Topten.JsonKit
-{
-    /// <summary>
-    /// Provides a reader for JSON data
-    /// </summary>
-    [Obfuscation(Exclude=true, ApplyToMembers=true)]
-    public interface IJsonReader
-    {
-        /// <summary>
-        /// Parses an object of specified type from the JSON stream
-        /// </summary>
-        /// <param name="type">The type to be parsed</param>
-        /// <returns>A reference to the loaded instance</returns>
-        object Parse(Type type);
-
-        /// <summary>
-        /// Parses an object of specified type from the JSON stream
-        /// </summary>
-        /// <typeparam name="T">The type to be parsed</typeparam>
-        /// <returns>A reference to the loaded instance</returns>
-        T Parse<T>();
-
-        /// <summary>
-        /// Parses from a JSON stream into an existing object instance
-        /// </summary>
-        /// <param name="into">The target object</param>
-        void ParseInto(object into);
-
-        /// <summary>
-        /// The current token in the input JSON stream
-        /// </summary>
-        Token CurrentToken { get; }
-
-        /// <summary>
-        /// Reads a literal value from the JSON stream
-        /// </summary>
-        /// <param name="converter">A converter function to convert the value</param>
-        /// <returns>The parsed and converted value</returns>
-        object ReadLiteral(Func<object, object> converter);
-
-        /// <summary>
-        /// Reads a dictinary from the input stream
-        /// </summary>
-        /// <param name="callback">A callback that will be invoked for each encountered dictionary key</param>
-        void ParseDictionary(Action<string> callback);
-
-        /// <summary>
-        /// Reads an array from the input stream
-        /// </summary>
-        /// <param name="callback">A callback that will be invoked as each array element is encounters</param>
-        void ParseArray(Action callback);
-
-        /// <summary>
-        /// Gets the literal kind of the current stream token
-        /// </summary>
-        /// <returns></returns>
-        LiteralKind GetLiteralKind();
-
-        /// <summary>
-        /// Gets a string literal from the JSON stream
-        /// </summary>
-        /// <returns></returns>
-        string GetLiteralString();
-
-        /// <summary>
-        /// Moves to the next token in the input stream
-        /// </summary>
-        void NextToken();
-    }
-
+using System.Reflection;
+
+
+namespace Topten.JsonKit
+{
+    /// <summary>
+    /// Provides a reader for JSON data
+    /// </summary>
+    [Obfuscation(Exclude=true, ApplyToMembers=true)]
+    public interface IJsonReader
+    {
+        /// <summary>
+        /// Parses an object of specified type from the JSON stream
+        /// </summary>
+        /// <param name="type">The type to be parsed</param>
+        /// <returns>A reference to the loaded instance</returns>
+        object Parse(Type type);
+
+        /// <summary>
+        /// Parses an object of specified type from the JSON stream
+        /// </summary>
+        /// <typeparam name="T">The type to be parsed</typeparam>
+        /// <returns>A reference to the loaded instance</returns>
+        T Parse<T>();
+
+        /// <summary>
+        /// Parses from a JSON stream into an existing object instance
+        /// </summary>
+        /// <param name="into">The target object</param>
+        void ParseInto(object into);
+
+        /// <summary>
+        /// The current token in the input JSON stream
+        /// </summary>
+        Token CurrentToken { get; }
+
+        /// <summary>
+        /// Reads a literal value from the JSON stream
+        /// </summary>
+        /// <param name="converter">A converter function to convert the value</param>
+        /// <returns>The parsed and converted value</returns>
+        object ReadLiteral(Func<object, object> converter);
+
+        /// <summary>
+        /// Reads a dictinary from the input stream
+        /// </summary>
+        /// <param name="callback">A callback that will be invoked for each encountered dictionary key</param>
+        void ParseDictionary(Type keyType, Action<object> callback);
+
+        /// <summary>
+        /// Reads an array from the input stream
+        /// </summary>
+        /// <param name="callback">A callback that will be invoked as each array element is encounters</param>
+        void ParseArray(Action callback);
+
+        /// <summary>
+        /// Gets the literal kind of the current stream token
+        /// </summary>
+        /// <returns></returns>
+        LiteralKind GetLiteralKind();
+
+        /// <summary>
+        /// Gets a string literal from the JSON stream
+        /// </summary>
+        /// <returns></returns>
+        string GetLiteralString();
+
+        /// <summary>
+        /// Moves to the next token in the input stream
+        /// </summary>
+        void NextToken();
+    }
+
     /// <summary>
     /// Helper functions for working with IJsonReader
-    /// </summary>
+    /// </summary>
     public static class IJsonReaderExtensions
     {
         /// <summary>
@@ -104,6 +104,26 @@ namespace Topten.JsonKit
             return (T)ReadLiteralNumber(reader, typeof(T));
         }
 
+
+        /// <summary>
+        /// Reads a dictionary from the input stream
+        /// </summary>
+        /// <param name="callback">A callback that will be invoked for each encountered dictionary key</param>
+        public static void ParseDictionary(this IJsonReader reader, Action<string> callback)
+        {
+            reader.ParseDictionary<string>(callback);
+        }
+
+        /// <summary>
+        /// Reads a dictionary from the input stream
+        /// </summary>
+        /// <param name="callback">A callback that will be invoked for each encountered dictionary key</param>
+        public static void ParseDictionary<T>(this IJsonReader reader, Action<T> callback)
+        {
+            reader.ParseDictionary(typeof(T), (o) => callback((T)o));
+        }
+
+
         /// <summary>
         /// Read a literal number
         /// </summary>
@@ -140,7 +160,7 @@ namespace Topten.JsonKit
                         return val;
                     }
             }
-            throw new InvalidDataException("expected a numeric literal");
+            throw new InvalidDataException("expected a numeric literal");
         }
-    }
-}
+    }
+}

+ 30 - 16
Topten.JsonKit/JsonReader.cs

@@ -68,7 +68,17 @@ namespace Topten.JsonKit
                 if (reader.CurrentToken == Token.OpenSquare)
                     throw new CancelReaderException();
                 return reader.ReadLiteral(literal => Convert.FromBase64String((string)Convert.ChangeType(literal, typeof(string), CultureInfo.InvariantCulture)));
-            });
+            });
+            _parsers.Set(typeof(Guid), (reader, type) =>
+            {
+                return (Guid)reader.ReadLiteral((val) =>
+                {
+                    if (val is string str)
+                        return new Guid(str);
+                    throw new InvalidDataException("Expected string guid");
+                });
+            });
+
         }
 
         /// <summary>
@@ -227,10 +237,10 @@ namespace Topten.JsonKit
                     _tokenizer.Skip(Token.OpenBrace);
 
                     // First pass to work out type
-                    ParseDictionaryKeys(key =>
+                    ParseDictionaryKeys(typeof(string), key =>
                     {
                         // Try to instantiate the object
-                        into = factory(this, key);
+                        into = factory(this, (string)key);
                         return into == null;
                     });
 
@@ -322,7 +332,7 @@ namespace Topten.JsonKit
             if (_tokenizer.CurrentToken == Token.OpenBrace && (type.IsAssignableFrom(typeof(IDictionary<string, object>))))
             {
                 var container = (new ExpandoObject()) as IDictionary<string, object>;
-                ParseDictionary(key =>
+                this.ParseDictionary(key =>
                 {
                     container[key] = Parse(typeof(Object));
                 });
@@ -404,9 +414,9 @@ namespace Topten.JsonKit
                 // Parse it
                 IDictionary dict = (IDictionary)into;
                 dict.Clear();
-                ParseDictionary(key =>
+                this.ParseDictionary(typeKey, key =>
                 {
-                    dict.Add(Convert.ChangeType(key, typeKey), Parse(typeValue));
+                    dict.Add(key, Parse(typeValue));
                 });
 
                 return;
@@ -435,7 +445,7 @@ namespace Topten.JsonKit
             if (objDict != null)
             {
                 objDict.Clear();
-                ParseDictionary(key =>
+                this.ParseDictionary(key =>
                 {
                     objDict[key] = Parse(typeof(Object));
                 });
@@ -490,42 +500,46 @@ namespace Topten.JsonKit
         }
 
         /// <inheritdoc />
-        public void ParseDictionary(Action<string> callback)
+        public void ParseDictionary(Type typeKey, Action<object> callback)
         {
             _tokenizer.Skip(Token.OpenBrace);
-            ParseDictionaryKeys(key => { callback(key); return true; });
+            ParseDictionaryKeys(typeKey, key => { callback(key); return true; });
             _tokenizer.Skip(Token.CloseBrace);
         }
 
         // Parse dictionary keys, calling callback for each one.  Continues until end of input
         // or when callback returns false
-        private void ParseDictionaryKeys(Func<string, bool> callback)
+        private void ParseDictionaryKeys(Type typeKey, Func<object, bool> callback)
         {
             // End?
             while (_tokenizer.CurrentToken != Token.CloseBrace)
             {
                 // Parse the key
-                string key = null;
+                object key = null;
                 if (_tokenizer.CurrentToken == Token.Identifier && (_options & JsonOptions.StrictParser)==0)
                 {
-                    key = _tokenizer.String;
+                    if (typeKey != typeof(string))
+                    {
+                        throw new NotImplementedException("Identifier keys can only be used with dictionaries with string keys");
+                    }
+                    key = _tokenizer.String;
+                    _tokenizer.NextToken();
                 }
-                else if (_tokenizer.CurrentToken == Token.Literal && _tokenizer.LiteralKind == LiteralKind.String)
+                else if (_tokenizer.CurrentToken == Token.Literal)
                 {
-                    key = (string)_tokenizer.LiteralValue;
+                    key = Parse(typeKey);
                 }
                 else
                 {
                     throw new InvalidDataException("syntax error, expected string literal or identifier");
                 }
-                _tokenizer.NextToken();
                 _tokenizer.Skip(Token.Colon);
 
                 // Remember current position
                 var pos = _tokenizer.CurrentTokenPosition;
 
                 // Call the callback, quit if cancelled
-                _contextStack.Add(key);
+                _contextStack.Add(key.ToString());
                 bool doDefaultProcessing = callback(key);
                 _contextStack.RemoveAt(_contextStack.Count-1);
                 if (!doDefaultProcessing)