/// <summary> /// Get the alias of the specified type, writing the type format description /// to the stream if it has not already been done for this type /// </summary> /// <param name="type"></param> /// <returns></returns> protected string SerializeType(Type type) { if (_Aliases.ContainsKey(type)) { return(_Aliases[type]); } else { //Generate alias: string abbreviation = type.Name.TruncatePascal(6); if (type.Name.EndsWith("[]")) { abbreviation = abbreviation.OverwriteEnd("[]"); } string alias = abbreviation; int i = 2; while (_Format.ContainsKey(alias)) { alias = abbreviation + i; i++; } _Aliases.Add(type, alias); var format = new TypeFieldsFormat(alias, type); _Format.Add(alias, format); _Writer.WriteLine(ToFormatDescription(format)); return(alias); } }
/// <summary> /// Parse a line of text as a data format /// </summary> /// <param name="line"></param> /// <returns></returns> public TypeFieldsFormat ReadFormat(string line, AlertLog log = null) { TypeFieldsFormat result; int i = FORMAT.Length; string alias = line.NextChunk(ref i, KEY_SEPARATOR); string typeName = line.NextChunk(ref i, OPEN_DATABLOCK); Type type = GetType(typeName); var fields = new List <FieldInfo>(); if (type != null) { while (i < line.Length) { string fieldName = line.NextChunk(ref i, SEPARATOR, CLOSE_DATABLOCK); if (!string.IsNullOrEmpty(fieldName)) { FieldInfo field = type.GetBaseField(fieldName); //TODO: check for mapped fields if null if (field == null) { log?.RaiseAlert("FNF " + fieldName + " on " + typeName, "Field '" + fieldName + "' cannot be found on type '" + type.Name + "'.", AlertLevel.Warning); } fields.Add(field); } } result = new TypeFieldsFormat(alias, type, fields); if (_Format != null) { _Format.Add(alias, result); } if (_Aliases != null) { _Aliases.Add(type, alias); } return(result); } else { return(null); } }
/*protected void SerializeItemX(object source) * { * if (source != null) * { * Type type = source.GetType(); * * if (type.IsPrimitive) * { * _Writer.Write(source.ToString()); * } * else if (type.IsAssignableFrom(typeof(string))) * { * _Writer.Write(source.ToString()); * } * else if (type.IsSerializable) * { * if (source is IUnique) * { * _Writer.Write(((IUnique)source).GUID); * _Writer.Write(KEY_SEPARATOR); * } * * string typeName = type.FullName; * * _Writer.Write(typeName); * * _Writer.Write(OPEN_DATABLOCK); * * TypeFieldsFormat format = null; * if (!_Format.ContainsKey(typeName)) * { * format = new TypeFieldsFormat(type, type.GetAllFields(true)); * // Add type to format: * _Format.Add(typeName, format); * } * else * format = _Format[typeName]; * * int valueCount = 0; * * // Write fields: * foreach (FieldInfo field in format.Fields) * { * if (valueCount > 0) _Writer.Write(SEPARATOR); * * object value = field.GetValue(source); * WriteValue(value, sb); * * valueCount++; * } * * // Write items: * if (type.IsArray) * { * foreach (object item in (IEnumerable)source) * { * if (valueCount > 0) _Writer.Write(SEPARATOR); * * WriteValue(item); * * valueCount++; * } * } * * _Writer.Write(CLOSE_DATABLOCK); * * } * * } * }*/ /// <summary> /// Construct a format descriptor from a type name and a list of serialisable fields /// </summary> /// <param name="typeName">The type's FullName</param> /// <param name="fields">all fields of the type which are to be serialized</param> /// <returns></returns> public string ToFormatDescription(TypeFieldsFormat format) { var sb = new StringBuilder(); sb.Append(FORMAT); sb.Append(format.Alias); sb.Append(KEY_SEPARATOR); sb.Append(format.Type.AssemblyQualifiedName); sb.Append(OPEN_DATABLOCK); for (int i = 0; i < format.Fields.Count; i++) { if (i > 0) { sb.Append(SEPARATOR); } FieldInfo field = format.Fields[i]; sb.Append(field.Name); } sb.Append(CLOSE_DATABLOCK); return(sb.ToString()); }
protected void PopulateFields(ref object target, TypeFieldsFormat format, ref int i, string line, AlertLog log) { string chunk; int j = 0; IList items = null; //Items for array reinstantiation object currentKey = null; //Current key object for Dictionaries while (i < line.Length) // && j < format.Fields.Count()) { char c; chunk = line.NextChunk(out c, ref i, SEPARATOR, OPEN_DATABLOCK, CLOSE_DATABLOCK, KEY_SEPARATOR); if (c == SEPARATOR || c == CLOSE_DATABLOCK) { // Chunk is simple value if (!string.IsNullOrEmpty(chunk)) { if (j < format.Fields.Count()) { FieldInfo fI = format.Fields[j]; object value = String2Object(chunk, fI.FieldType); fI.SetValue(target, value); } else if (target is IDictionary && currentKey != null) //Dictionary value { //Add to dictionary IDictionary dic = (IDictionary)target; Type[] arguments = dic.GetType().GetGenericArguments(); if (arguments.Length > 1) { object value = String2Object(chunk, arguments[1]); //TODO: What if value is complex object? dic[currentKey] = value; } currentKey = null; } else if (format.Type.IsList()) { Type type = format.Type.GetElementType(); if (format.Type.ContainsGenericParameters) { type = format.Type.GenericTypeArguments[0]; } object value = String2Object(chunk, type); if (format.Type.IsArray) { if (items == null) { Type listType = typeof(List <>).MakeGenericType(type); items = Activator.CreateInstance(listType) as IList; //TODO!!! Create list of required type! } items.Add(value); } else { IList list = (IList)target; list.Add(value); } //TODO } } j++; if (c == CLOSE_DATABLOCK) { // The current object definition is finished - can step out if (items != null) { Type type = format.Type.GetElementType(); Array targetArray = Array.CreateInstance(type, items.Count); for (int k = 0; k < items.Count; k++) { targetArray.SetValue(items[k], k); } target = targetArray; } return; } } else if (c == OPEN_DATABLOCK) { // Chunk is the type alias of an embedded object if (_Format.ContainsKey(chunk)) { TypeFieldsFormat subFormat = _Format[chunk]; object value = null; if (!subFormat.Type.IsArray) { value = subFormat.Type.Instantiate(); } else { value = Activator.CreateInstance(subFormat.Type, new object[] { 0 }); } PopulateFields(ref value, subFormat, ref i, line, log); if (j < format.Fields.Count) { // Is sub-object belonging to a field FieldInfo fI = format.Fields[j]; fI.SetValue(target, value); } else if (target is Array) { if (items == null) { items = new List <object>(); //TODO: Create list of specified type } items.Add(value); } else if (target is IDictionary && currentKey != null) //Dictionary value { IDictionary dic = (IDictionary)target; dic[currentKey] = value; currentKey = null; } else if (target is IDictionary && line[i] == KEY_SEPARATOR) //Dictionary key { currentKey = value; i++; //Skip the key separator character } else if (target is IList) { // Is an entry in the target collection IList list = (IList)target; list.Add(value); } //j++; //i++; //Skip the next separator? } else { log?.RaiseAlert("FNF" + chunk, "Formatting data for type alias '" + chunk + "' not found.", AlertLevel.Warning); } } else if (c == KEY_SEPARATOR && target is IDictionary) { // Is a key for a dictionary: Type[] genTypes = target.GetType().GetGenericArguments(); currentKey = String2Object(chunk, genTypes[0]); } else { // Line is not closed correctly - must be a multiline string // TODO } } }
public IUnique Deserialize(Stream stream, AlertLog log = null) { IUnique result = null; _Format = new Dictionary <string, TypeFieldsFormat>(); _Aliases = new Dictionary <Type, string>(); _Uniques = new UniquesCollection(); _Reader = new StreamReader(stream); // First, read through once to end: log?.RaiseAlert("FORM", "Reading format data..."); string line; int lineCount = 0; while ((line = _Reader.ReadLine()) != null) { lineCount++; if (line.StartsWith(FORMAT)) { ReadFormat(line, log); } else if (line.StartsWith(DATA)) { // Initial pass: Create object int i = DATA.Length; string guid = line.NextChunk(ref i, KEY_SEPARATOR); string typeAlias = line.NextChunk(ref i, OPEN_DATABLOCK); if (_Format.ContainsKey(typeAlias)) { TypeFieldsFormat format = _Format[typeAlias]; IUnique unique = format.Type.Instantiate() as IUnique;//FormatterServices.GetUninitializedObject(format.Type) as IUnique; if (unique is IUniqueWithModifiableGUID) { var uniqueMG = (IUniqueWithModifiableGUID)unique; uniqueMG.SetGUID(new Guid(guid)); } else { FieldInfo fI = format.Type.GetBaseField("_GUID"); // Will not work if backing field is named differently! fI.SetValue(unique, new Guid(guid)); } _Uniques.Add(unique); if (result == null) { result = unique; // Set primary output object } } else { log?.RaiseAlert("FNF" + typeAlias, "Formatting data for type alias '" + typeAlias + "' not found.", AlertLevel.Warning); } } } log?.RaiseAlert("FORM", "Format data read."); // Next: Second pass - populate fields with data // Rewind: stream.Seek(0, SeekOrigin.Begin); _Reader = new StreamReader(stream); int lineNum = 0; while ((line = _Reader.ReadLine()) != null) { log?.RaiseAlert("DAT", "Reading data...", (double)lineNum / (double)lineCount); if (line.StartsWith(DATA)) { // Initial pass: Create object int i = DATA.Length; string guid = line.NextChunk(ref i, KEY_SEPARATOR); string typeAlias = line.NextChunk(ref i, OPEN_DATABLOCK); if (_Format.ContainsKey(typeAlias)) { TypeFieldsFormat format = _Format[typeAlias]; object unique = _Uniques[new Guid(guid)]; PopulateFields(ref unique, format, ref i, line, log); } } lineNum++; } log?.RaiseAlert("DAT", "Reading data complete.", 1.0); log?.RaiseAlert("FIN", "Finalising..."); // Finally: Call OnDeserialized function on each object foreach (var unique in _Uniques) { MethodInfo mInfo = unique.GetType().GetOnDeserializedMethod(); if (mInfo != null) { mInfo.Invoke(unique, new object[] { new StreamingContext() }); } // TODO: Pass in populated streamingcontext arguments? } log?.RaiseAlert("FIN", "Finalised"); return(result); }
protected void WriteObject(object source, StringBuilder sb) { Type type = source.GetType(); if (type.IsPrimitive || type.IsAssignableFrom(typeof(string)) || type == typeof(Guid)) { sb.Append(source.ToString()); } else if (type.IsSerializable) { string typeAlias = SerializeType(type); TypeFieldsFormat format = _Format[typeAlias]; sb.Append(typeAlias); sb.Append(OPEN_DATABLOCK); int valueCount = 0; // Write fields: foreach (FieldInfo field in format.Fields) { if (valueCount > 0) { sb.Append(SEPARATOR); } object value = field.GetValue(source); WriteValue(value, sb); valueCount++; } // Write items: if (type.IsArray) { foreach (object item in (IEnumerable)source) { if (valueCount > 0) { sb.Append(SEPARATOR); } WriteValue(item, sb); valueCount++; } } else if (type.IsStandardDictionary()) { IDictionary dic = (IDictionary)source; foreach (DictionaryEntry item in dic) { if (valueCount > 0) { sb.Append(SEPARATOR); } WriteValue(item.Key, sb); sb.Append(KEY_SEPARATOR); WriteValue(item.Value, sb); valueCount++; } } sb.Append(CLOSE_DATABLOCK); } else { //Type not serializable - throw warning: RaiseError("Type '" + type.FullName + "' is not marked as serializable and could not be saved."); } }