/// <summary> /// Read existing items in an existing Dictionary instance. /// Used with Dictionaries of specific things which may or may not be present in the file, like Table.Columns. /// </summary> /// <typeparam name="T">Type of values in Dictionary</typeparam> /// <param name="reader">ITreeReader to read from</param> /// <param name="dictionary">Dictionary containing items to read</param> /// <param name="throwOnUnknown">True to throw for property name not in Dictionary, false to quietly skip over it</param> public static void ReadDictionaryItems <T>(this ITreeReader reader, Dictionary <string, T> dictionary, bool throwOnUnknown = true) where T : ITreeSerializable { reader.Expect(TreeToken.StartObject); reader.Read(); while (reader.TokenType == TreeToken.PropertyName) { string itemName = reader.ReadAsString(); reader.Read(); if (dictionary.TryGetValue(itemName, out T item)) { item.Read(reader); reader.Read(); } else { if (throwOnUnknown) { throw new IOException($"Found unknown {typeof(T).Name} property \"{itemName}\", expected one of \"{String.Join("; ", dictionary.Keys)}\" at {reader.Position:n0} using {reader.GetType().Name}."); } else { reader.Skip(); } } } reader.Expect(TreeToken.EndObject); }
/// <summary> /// ReadObject wraps the loop to read each property in an object and call the corresponding /// setter to set it. /// </summary> /// <remarks> /// This works for classes only, as the instance can't be passed by ref. /// Structs can serialize as arrays or directly implement the loop to decode themselves. /// Setters take a reference to the instance so that the Dictionary can be static per type, /// which is critical for acceptable performance. /// </remarks> /// <typeparam name="T">Type being deserialized</typeparam> /// <param name="reader">ITreeReader being read from</param> /// <param name="instance">T instance being initialized</param> /// <param name="setters">Dictionary of setter per field name</param> /// <param name="throwOnUnknown">Throw if property name not in setters found</param> public static void ReadObject <T>(this ITreeReader reader, T instance, Dictionary <string, Setter <T> > setters, bool throwOnUnknown = true) where T : ITreeSerializable { // Ensure object state reset before Read instance.Clear(); reader.Expect(TreeToken.StartObject); reader.Read(); while (reader.TokenType == TreeToken.PropertyName) { string propertyName = reader.ReadAsString(); reader.Read(); if (setters.TryGetValue(propertyName, out Setter <T> setter)) { setter(reader, instance); reader.Read(); } else { if (throwOnUnknown) { throw new IOException($"Found unknown {typeof(T).Name} property, \"{propertyName}\", expected one of \"{String.Join("; ", setters.Keys)}\" at {reader.Position:n0} using {reader.GetType().Name}."); } else { reader.Skip(); } } } reader.Expect(TreeToken.EndObject); // EndObject must be left for caller to handle }
public static Dictionary <int, T> ReadIntDictionary <T>(this ITreeReader reader, Func <T> ctor) where T : ITreeSerializable { if (reader.TokenType == TreeToken.Null) { return(null); } Dictionary <int, T> result = new Dictionary <int, T>(); reader.Expect(TreeToken.StartArray); reader.Read(); int[] keys = reader.ReadBlockArray <int>(); reader.Read(); reader.Expect(TreeToken.StartArray); reader.Read(); for (int i = 0; i < keys.Length; ++i) { int key = keys[i]; T value = ctor(); value.Read(reader); result[key] = value; reader.Read(); } reader.Expect(TreeToken.EndArray); reader.Read(); reader.Expect(TreeToken.EndArray); return(result); }
public static Dictionary <string, T> ReadStringDictionary <T>(this ITreeReader reader, Func <T> ctor) where T : ITreeSerializable { if (reader.TokenType == TreeToken.Null) { return(null); } Dictionary <string, T> result = new Dictionary <string, T>(); reader.Expect(TreeToken.StartObject); reader.Read(); while (reader.TokenType == TreeToken.PropertyName) { string key = reader.ReadAsString(); reader.Read(); T value = ctor(); value.Read(reader); result[key] = value; reader.Read(); } reader.Expect(TreeToken.EndObject); return(result); }
public void CreateTree() { _reader.Read(); var lines = _reader.Lines; foreach (string line in lines) { _binaryTree.AddLineBottom(CreateList(line)); } }
private static void TestIntegers(TreeFormat format) { TreeSerializationSettings settings = new TreeSerializationSettings() { LeaveStreamOpen = true }; using (MemoryStream stream = new MemoryStream()) { using (ITreeWriter writer = Writer(format, stream, settings)) { writer.WriteStartArray(); for (int i = -1; i < 300; i += 7) { writer.WriteValue(i); } writer.WriteEndArray(); } long bytesWritten = stream.Position; stream.Seek(0, SeekOrigin.Begin); using (ITreeReader reader = Reader(format, stream, settings)) { Assert.Equal(TreeToken.StartArray, reader.TokenType); for (int i = -1; i < 300; i += 7) { Assert.True(reader.Read()); Assert.Equal(TreeToken.Integer, reader.TokenType); Assert.Equal(i, reader.ReadAsInt32()); } Assert.True(reader.Read()); Assert.Equal(TreeToken.EndArray, reader.TokenType); Assert.False(reader.Read()); } Assert.Equal(bytesWritten, stream.Position); } }
public static void VerifySkip <T>(T value, TreeFormat format) where T : ITreeSerializable { // Test serialization details using (MemoryStream stream = new MemoryStream()) { TreeSerializationSettings settings = new TreeSerializationSettings() { LeaveStreamOpen = true }; using (ITreeWriter writer = Writer(format, stream, settings)) { value.Write(writer); } long bytesWritten = stream.Position; // Read tokens individually and verify 'None' returned at end stream.Seek(0, SeekOrigin.Begin); using (ITreeReader reader = Reader(format, stream, settings)) { while (reader.Read()) { // Verify each token type is coming back properly (no reading random bytes) Assert.True((byte)reader.TokenType <= (byte)TreeToken.BlockArray); } Assert.Equal(TreeToken.None, reader.TokenType); Assert.Equal(bytesWritten, stream.Position); } // Verify Skip once skips everything (each ITreeSerializable must be one value or one root array or object stream.Seek(0, SeekOrigin.Begin); using (ITreeReader reader = Reader(format, stream, settings)) { reader.Skip(); Assert.Equal(TreeToken.None, reader.TokenType); Assert.Equal(bytesWritten, stream.Position); } // For objects, verify each property can be skipped correctly // Each Skip should read the value, so that the next token is the next PropertyName stream.Seek(0, SeekOrigin.Begin); using (ITreeReader reader = Reader(format, stream, settings)) { if (reader.TokenType == TreeToken.StartObject) { Empty empty = new Empty(); empty.Read(reader); Assert.Equal(bytesWritten, stream.Position); } } } }
public static List <T> ReadList <T>(this ITreeReader reader, Func <T> ctor) where T : ITreeSerializable { if (reader.TokenType == TreeToken.Null) { return(null); } List <T> result = new List <T>(); reader.Expect(TreeToken.StartArray); reader.Read(); while (reader.TokenType != TreeToken.EndArray) { T item = ctor(); item.Read(reader); result.Add(item); reader.Read(); } return(result); }
/// <summary> /// Skip the current token. /// Reads a single token, or skips over an entire subtree for containers. /// </summary> /// <param name="reader">ITreeReader to skip next token from</param> public static void Skip(this ITreeReader reader) { int depth = 0; do { switch (reader.TokenType) { case TreeToken.StartArray: case TreeToken.StartObject: depth++; break; case TreeToken.EndArray: case TreeToken.EndObject: depth--; break; } reader.Read(); } while (depth > 0); }