private static object DeserializeObjectWithProperties(TextReader r, Type type, ObjectGraphContext context, StringBuilder log) { // create object and add it to our context var o = type.Instantiate(); context.Add(o); // properties count - need to create object and populate properties int count; string s = r.ReadTo(':', log); if (!int.TryParse(s, out count)) { throw new SerializationException("Expected integer, got \"" + s + "\" when parsing property count."); } var dict = new SafeDictionary <string, object>(); // deserialize the properties var props = ObjectGraphContext.GetKnownProperties(type, true); for (int i = 0; i < count; i++) { var pname = r.ReadTo(':', log).Trim(); if (type == typeof(Game.Objects.Civilization.Empire) && pname == "StoredResources") { } if (props.ContainsKey(pname)) { // TODO - get base class recursively, not just derived class and declaring type var prop = type.GetProperty(pname, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) ?? props[pname]; // get concrete type property in case it has DoNotSerialize and the abstract type doesn't if (prop != null) { if (prop.Name == "StarSystemNames") { } var data = Deserialize(r, prop.PropertyType, false, context, log); if (prop.HasAttribute <SerializationPriorityAttribute>()) { prop.SetValue(o, data); // TODO - use cached reflection lambdas } if (!prop.HasAttribute <DoNotSerializeAttribute>()) { dict[pname] = data; } } else { r.ReadToEndOfLine(';', log); // throw away this property, we don't need it } // if p is null or has do not serialize attribute, it must be data from an old version with different property names, so don't crash } } //propertySetterTasks.Add(Task.Factory.StartNew(() => //{ o.SetData(dict, context); //})); // clean up ReadSemicolon(r, type, log); return(o); }
private static object DeserializeStringifiedObject(TextReader r, Type type, ObjectGraphContext context, StringBuilder log) { object o; // read tag to see if it's actually a stringified object var fin = r.Read(); while (fin != 0 && char.IsWhiteSpace((char)fin)) { if (log != null) { log.Append((char)fin); } fin = r.Read(); } if (fin != 0 && log != null) { log.Append((char)fin); } if (fin == 's') { IStringifier stringifier = null; var t = type; while (stringifier == null && t != null) { stringifier = StringifierLibrary.Instance.All.SingleOrDefault(x => x.SupportedType == t); t = t.BaseType; } if (stringifier == null) { throw new Exception("Can't find stringifier to deserialize " + type); } var dummy = r.ReadTo(':', log); var val = r.ReadToEndOfLine(';', log); o = stringifier.Destringify(val); } else if (fin == 'p') { o = DeserializeObjectWithProperties(r, type, context, log); } else if (fin == 'i') { o = DeserializeObjectWithID(r, type, context, log); } else if (fin == 'n') { // null object! o = null; // clean up ReadSemicolon(r, type, log); } else if (fin == 'd') { o = DeserializeDictionary(r, type, context, log); } else if (fin == 'c') { o = DeserializeList(r, type, context, log); } else { throw new Exception("Unknown data tag " + fin + ", was expecting s/p/i/n/d/c."); } if (!context.KnownObjects.ContainsKey(type) || !context.KnownObjects[type].Contains(o)) { context.Add(o); } return(o); }
private static IEnumerable DeserializeDictionary(TextReader r, Type type, ObjectGraphContext context, StringBuilder log) { IEnumerable o; int size; var sizeStr = r.ReadTo(':', log); if (!int.TryParse(sizeStr, out size)) { throw new SerializationException("Expected integer, got \"" + sizeStr + "\" when parsing collection size."); } var coll = type.Instantiate(); context.Add(coll); var adder = type.GetMethods().Single(m => m.Name == "Add" && m.GetParameters().Length == 2); Type itemType; if (type.GetGenericArguments().Count() == 2) { itemType = typeof(KeyValuePair <,>).MakeGenericType(type.GetGenericArguments()); } else if (type == typeof(DynamicDictionary)) { itemType = typeof(KeyValuePair <object, object>); } else { // HACK - Resources inherits from a dictionary type itemType = typeof(KeyValuePair <,>).MakeGenericType(type.BaseType.GetGenericArguments()); } var collParm = Expression.Parameter(typeof(object), "coll"); var keyParm = Expression.Parameter(typeof(object), "key"); var valParm = Expression.Parameter(typeof(object), "val"); var keyprop = ObjectGraphContext.GetKnownProperties(itemType, true)["Key"]; var valprop = ObjectGraphContext.GetKnownProperties(itemType, true)["Value"]; Delegate lambdaAdder; if (ObjectGraphContext.CollectionAdders[type] == null) { // lambda has not been created yet, so create it ObjectGraphContext.CollectionAdders[type] = Expression.Lambda(Expression.Call( Expression.Convert(collParm, type), adder, Expression.Convert(keyParm, keyprop.PropertyType), Expression.Convert(valParm, valprop.PropertyType) ), collParm, keyParm, valParm).Compile(); } // get lambda lambdaAdder = ObjectGraphContext.CollectionAdders[type]; // load items and add them for (int i = 0; i < size; i++) { var key = Deserialize(r, keyprop.PropertyType, false, context, log); var val = Deserialize(r, valprop.PropertyType, false, context, log); lambdaAdder.DynamicInvoke(coll, key, val); } o = (IEnumerable)coll; // clean up ReadSemicolon(r, type, log); return(o); }
private static IEnumerable DeserializeList(TextReader r, Type type, ObjectGraphContext context, StringBuilder log) { IEnumerable o; int size; var sizeStr = r.ReadTo(':', log); if (!int.TryParse(sizeStr, out size)) { throw new SerializationException("Expected integer, got \"" + sizeStr + "\" when parsing collection size."); } var coll = Activator.CreateInstance(type); context.Add(coll); var adder = type.GetMethods().Single(m => m.Name == "Add" && m.GetParameters().Length == 1); Type itemType; if (typeof(DynamicDictionary).IsAssignableFrom(type)) { itemType = typeof(KeyValuePair <object, object>); } else if (type.GetGenericArguments().Length == 2) { // HACK - assume it's a dictionary, no real way to test itemType = typeof(KeyValuePair <,>).MakeGenericType(type.GetGenericArguments()); } else if (type.GetGenericArguments().Length == 1) { // HACK - assume it's a collection, no real way to test itemType = type.GetGenericArguments()[0]; } else { // no generic type? probably a list of objects? itemType = typeof(object); } var collParm = Expression.Parameter(type, "coll"); var objParm = Expression.Parameter(itemType, "obj"); Delegate lambdaAdder; if (ObjectGraphContext.CollectionAdders[type] == null) { // lambda has not been created yet, so create it try { ObjectGraphContext.CollectionAdders[type] = Expression.Lambda(Expression.Call( collParm, // the collection to add to adder, // the add method to call objParm), // the object to add collParm, objParm).Compile(); } catch (Exception ex) { throw new SerializationException("Could not create lambda to add {0} items to {1}.".F(itemType, type), ex); } } // get lambda lambdaAdder = ObjectGraphContext.CollectionAdders[type]; // load items and add them for (int i = 0; i < size; i++) { var item = Deserialize(r, itemType, false, context, log); lambdaAdder.DynamicInvoke(coll, item); } o = (IEnumerable)coll; // clean up ReadSemicolon(r, type, log); return(o); }
private static Array DeserializeArray(TextReader r, Type type, ObjectGraphContext context, StringBuilder log) { // arrays Array o; // read bounds or id number var fin = r.Read(); while (fin != 0 && char.IsWhiteSpace((char)fin)) { if (log != null) { log.Append((char)fin); } fin = r.Read(); } if (fin != 0 && log != null) { log.Append((char)fin); } if (fin == 'a') { var boundsStrs = r.ReadTo(':', log).Split(','); if (boundsStrs.Length < 1) { throw new SerializationException("Arrays cannot have zero dimensions."); } if (boundsStrs.Length == 1) { int min, max; var bounds1Strs = boundsStrs[0].Split('_'); if (!int.TryParse(bounds1Strs[0], out min)) { throw new SerializationException("Expected integer, got \"" + bounds1Strs[0] + "\" when parsing array bounds."); } if (!int.TryParse(bounds1Strs[1], out max)) { throw new SerializationException("Expected integer, got \"" + bounds1Strs[1] + "\" when parsing array bounds."); } // HACK - figure out how to set min and max bounds, in case it matters (VB?) var array = Array.CreateInstance(type.GetElementType(), max - min + 1); for (int i = min; i <= max; i++) { array.SetValue(Deserialize(r, type.GetElementType(), false, context, log), i); } o = array; } else if (boundsStrs.Length == 2) { int min1, max1, min2, max2; var bounds1Strs = boundsStrs[0].Split('_'); var bounds2Strs = boundsStrs[1].Split('_'); if (!int.TryParse(bounds1Strs[0], out min1)) { throw new SerializationException("Expected integer, got \"" + bounds1Strs[0] + "\" when parsing array bounds."); } if (!int.TryParse(bounds1Strs[1], out max1)) { throw new SerializationException("Expected integer, got \"" + bounds1Strs[1] + "\" when parsing array bounds."); } if (!int.TryParse(bounds2Strs[0], out min2)) { throw new SerializationException("Expected integer, got \"" + bounds2Strs[0] + "\" when parsing array bounds."); } if (!int.TryParse(bounds2Strs[1], out max2)) { throw new SerializationException("Expected integer, got \"" + bounds2Strs[1] + "\" when parsing array bounds."); } // HACK - figure out how to set min and max bounds, in case it matters (VB?) var array = Array.CreateInstance(type.GetElementType(), max1 - min1 + 1, max2 - min2 + 1); for (int x = min1; x <= max1; x++) { for (int y = min2; y <= max2; y++) { array.SetValue(Deserialize(r, type.GetElementType(), false, context, log), x, y); } } o = array; context.Add(o); } else { throw new SerializationException("Arrays with more than two dimensions are not supported."); } // clean up ReadSemicolon(r, type, log); } else if (fin == 'i') { // ID - need to find known object int id; string s = r.ReadToEndOfLine(';', log); if (!int.TryParse(s, out id)) { throw new SerializationException("Expected integer, got \"" + s + "\" when parsing object ID."); } // do we have it? if (!context.KnownObjects.ContainsKey(type) || context.KnownObjects[type].Count <= id) { throw new SerializationException("No known object of type " + type + " has an ID of " + id + "."); } // found it! o = (Array)context.KnownObjects[type][id]; } else if (fin == 'n') { // null object! o = null; // clean up ReadSemicolon(r, type, log); } else { throw new SerializationException("Expected 'a'/'i'/'n', got '" + (char)fin + "' when parsing " + type + "."); } return(o); }