// ---- Serialization ---- private void CollectArrayContents(object obj) { // Put strings into the string array. if (obj is string) { _stringArray.Add((string)obj); return; } // Put paths into the path array (if supported). if (Settings.SupportPaths) { if (obj is List <ByamlPathPoint> ) { _pathArray.Add((List <ByamlPathPoint>)obj); return; } } // Traverse through arrays if the element type is of interest. if (obj is IList) { IList objArray = (IList)obj; Type elementType = objArray.GetType().GetTypeInfo().GetElementType(); if (elementType == typeof(string) || elementType is IList || IsTypeByamlObject(elementType)) { foreach (object element in objArray) { CollectArrayContents(element); } } return; } // Traverse through other types decorated with the ByamlObjectAttribute and collect their keys and values. Type type = obj.GetType(); if (IsTypeByamlObject(type)) { ByamlObjectInfo objectInfo; if (!_byamlObjectInfos.TryGetValue(type, out objectInfo)) { objectInfo = new ByamlObjectInfo(type); _byamlObjectInfos.Add(type, objectInfo); } // Query the custom values and remember them for when to actually store them in the BYAML. if (objectInfo.ImplementsByamlSerializable) { IByamlSerializable byamlSerializable = (IByamlSerializable)obj; Dictionary <string, object> customMembers = new Dictionary <string, object>(); byamlSerializable.SerializeByaml(customMembers); foreach (KeyValuePair <string, object> customMember in customMembers) { _nameArray.Add(customMember.Key); CollectArrayContents(customMember.Value); } if (customMembers.Count > 0) { _customMembers.Add(obj, customMembers); } } // Go through the default members. foreach (KeyValuePair <string, ByamlMemberInfo> member in objectInfo.Members) { object value = member.Value.GetValue(obj); if (value != null || !member.Value.Optional) { _nameArray.Add(member.Key); if (value != null) { CollectArrayContents(value); } } } return; } }
private void WriteDictionary(BinaryDataWriter writer, object obj) { // Create a string-object dictionary out of the members. Dictionary <string, object> dictionary = new Dictionary <string, object>(); // Add the custom members if any have been created when collecting node contents previously. Dictionary <string, object> customMembers; if (_customMembers.TryGetValue(obj, out customMembers)) { foreach (KeyValuePair <string, object> customMember in customMembers) { dictionary.Add(customMember.Key, customMember.Value); } } // Add the ByamlMemberAttribute decorated members. ByamlObjectInfo objectInfo = _byamlObjectInfos[obj.GetType()]; foreach (KeyValuePair <string, ByamlMemberInfo> member in objectInfo.Members) { object value = member.Value.GetValue(obj); if (value != null || !member.Value.Optional) { dictionary.Add(member.Key, value); } } // Dictionaries need to be sorted ordinally by key. var sortedDict = dictionary.Values.Zip(dictionary.Keys, (Value, Key) => new { Key, Value }) .OrderBy(x => x.Key, StringComparer.Ordinal).ToList(); WriteTypeAndElementCount(writer, ByamlNodeType.Dictionary, dictionary.Count); // Write the key-value pairs. Dictionary <Offset, object> offsetElements = new Dictionary <Offset, object>(); foreach (var keyValuePair in sortedDict) { string key = keyValuePair.Key; object element = keyValuePair.Value; // Get the index of the key string in the file's name array and write it together with the type. uint keyIndex = (uint)_nameArray.IndexOf(key); ByamlNodeType nodeType = element == null ? ByamlNodeType.Null : GetNodeType(element.GetType()); if (Settings.ByteOrder == ByteOrder.BigEndian) { writer.Write(keyIndex << 8 | (uint)nodeType); } else { writer.Write(keyIndex | (uint)nodeType << 24); } // Write the elements. Complex types are just offsets, primitive types are directly written as values. if (nodeType == ByamlNodeType.Array || nodeType == ByamlNodeType.Dictionary) { offsetElements.Add(writer.ReserveOffset(), element); } else { WritePrimitiveType(writer, nodeType, element); } } // Write the array or dictionary elements and satisfy their offsets. foreach (KeyValuePair <Offset, object> offsetElement in offsetElements) { WriteArrayOrDictionary(writer, offsetElement.Key, offsetElement.Value); } }
private object ReadDictionary(BinaryDataReader reader, Type type, int length) { // Get the information required to serialize this type (for Nullables take the underlying type). Type nullableType = Nullable.GetUnderlyingType(type); if (nullableType != null) { type = nullableType; } ByamlObjectInfo objectInfo; if (!_byamlObjectInfos.TryGetValue(type, out objectInfo)) { objectInfo = new ByamlObjectInfo(type); _byamlObjectInfos.Add(type, objectInfo); } // Instantiate the type and read in the elements. Use a given instance as the root if available. object instance; if (_instance == null) { instance = Activator.CreateInstance(type, true); } else { instance = _instance; _instance = null; } // Collect them in a dictionary for custom deserialization. Dictionary <string, object> dictionary = new Dictionary <string, object>(); for (int i = 0; i < length; i++) { uint indexAndType = reader.ReadUInt32(); int nodeNameIndex = (int)Get3MsbBytes(indexAndType); ByamlNodeType nodeType = (ByamlNodeType)Get1MsbByte(indexAndType); string key = _nameArray[nodeNameIndex]; // Find a member for it to map the value to. object value; ByamlMemberInfo member; if (objectInfo.Members.TryGetValue(key, out member)) { // The key could be mapped to a member, read it as the member's type. value = ReadValue(reader, member.Type, nodeType); member.SetValue(instance, value); } else { // If the key could not be mapped to a member, add it to the dictionary for custom deserialization. //Debug.WriteLine($"No member in {type.Name} found to map key \"{key}\" to."); value = ReadValue(reader, nodeType.GetInstanceType(), nodeType); } dictionary.Add(key, value); } // Call IByamlSerializable methods if the interface was implemented. if (objectInfo.ImplementsByamlSerializable) { IByamlSerializable byamlSerializable = (IByamlSerializable)instance; byamlSerializable.DeserializeByaml(dictionary); } // Check if any required fields were not filled. foreach (ByamlMemberInfo member in objectInfo.Members.Values) { if (!member.Optional && member.GetValue(instance) == null) { throw new ByamlException( $"Member {type.Name}.{member.MemberInfo.Name} is not optional, but has not been deserialized."); } } return(instance); }