private void WriteElement(IElement element, XmlWriter writer, FileStream stream) { writer.WriteStartElement(null, element.ElementName, null); Type elementType = element.GetType(); BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; MemberInfo[] members = elementType.GetMembers(flags). Where(x => x.GetCustomAttribute <Attr>() != null).ToArray(); foreach (MemberInfo member in members) { Attr attr = member.GetCustomAttribute <Attr>(); object value = member is PropertyInfo prop?prop.GetValue(element) : (member is FieldInfo field ? field.GetValue(element) : null); if (!attr.Required && value == elementType.GetDefaultValue()) { continue; } writer.WriteAttributeString(null, attr.AttributeName, null, value.ToString()); } if (element is IStringElement stringEntry) { string value = stringEntry.GenericStringContent.WriteToString(); writer.WriteString(value); } else { IOrderedEnumerable <IElement> orderedChildren = element.ChildElements.Values.SelectMany(x => x).OrderBy(x => x.ElementIndex); foreach (IElement child in orderedChildren) { WriteElement(child, writer, stream); } } writer.WriteEndElement(); }
private IElement ParseElement( IElement entry, IElement parent, XmlReader reader, string version, ulong ignoreFlags, string parentTree, int elementIndex, FileStream stream) { Type elementType = entry.GetType(); entry.Parent = parent; entry.ElementIndex = elementIndex; entry.PreRead(); string parentElementName = reader.Name; if (string.IsNullOrEmpty(parentElementName)) { throw new Exception("Null parent element name."); } parentTree += parentElementName + "/"; entry.Tree = parentTree; if (reader.NodeType != XmlNodeType.Element) { WriteLine($"Encountered an unexpected node: {reader.Name} '{reader.NodeType.ToString()}'"); reader.Skip(); entry.PostRead(); return(entry); } if (entry.ParentType != typeof(IElement) && !entry.ParentType.IsAssignableFrom(parent.GetType())) { WriteLine($"Parent mismatch. {elementType.GetFriendlyName()} expected {entry.ParentType.GetFriendlyName()}, but got {parent.GetType().GetFriendlyName()}"); reader.Skip(); entry.PostRead(); return(entry); } if ((ignoreFlags & entry.TypeFlag) != 0) { reader.Skip(); entry.PostRead(); return(entry); } #region Read attributes MemberInfo[] members = entry.WantsManualRead ? null : elementType.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (reader.HasAttributes) { while (reader.MoveToNextAttribute()) { string name = reader.Name; string value = reader.Value; if (entry.WantsManualRead) { entry.ManualReadAttribute(name, value); } else { MemberInfo info = members.FirstOrDefault(x => { Attr a = x.GetCustomAttribute <Attr>(); return(a == null ? false : string.Equals(a.AttributeName, name, StringComparison.InvariantCultureIgnoreCase)); }); if (info == null) { WriteLine($"Attribute '{parentTree}[{name}]' not supported by parser. Value = '{value}'"); } else if (info is FieldInfo field) { field.SetValue(entry, value.ParseAs(field.FieldType)); } else if (info is PropertyInfo property) { property.SetValue(entry, value.ParseAs(property.PropertyType)); } } } } #endregion if (entry is IVersion v) { version = v.Version; } entry.OnAttributesRead(); #region Read child elements reader.MoveToElement(); if (entry is IStringElement stringEntry) { stringEntry.GenericStringContent = Activator.CreateInstance(stringEntry.GenericStringType) as BaseElementString; stringEntry.GenericStringContent.ReadFromString(reader.ReadElementContentAsString()); } else { if (reader.IsEmptyElement) { reader.Read(); } else { reader.ReadStartElement(); int childIndex = 0; ChildInfo[] childElements = entry.WantsManualRead ? null : elementType.GetCustomAttributesExt <Child>().Select(x => new ChildInfo(x)).ToArray(); MultiChildInfo[] multiChildElements = entry.WantsManualRead ? null : elementType.GetCustomAttributesExt <MultiChild>().Select(x => new MultiChildInfo(x)).ToArray(); //Read all child elements while (reader.NodeType != XmlNodeType.EndElement) { if (reader.NodeType != XmlNodeType.Element) { reader.Skip(); continue; } string elementName = reader.Name; if (string.IsNullOrEmpty(elementName)) { throw new Exception("Null element name."); } if (entry.WantsManualRead) { IElement e = entry.ManualReadChildElement(elementName, version); if (e == null) { //Console.WriteLine("Element '{0}' not supported by parser.", parentTree + elementName + "/"); reader.Skip(); } else { ParseElement(e, entry, reader, version, ignoreFlags, parentTree, childIndex, stream); } } else { bool isUnsupported = elementType.GetCustomAttributesExt <UnsupportedChild>(). Any(x => string.Equals(x.ElementName, elementName, StringComparison.InvariantCultureIgnoreCase)); if (isUnsupported) { WriteLine($"Element '{parentTree + elementName}/' not supported by parser."); reader.Skip(); } else { int typeIndex = -1; foreach (ChildInfo child in childElements) { typeIndex = Array.FindIndex(child.ElementNames, name => name.Matches(elementName, version)); //If no exact name matches, find a null name child element. //This means the class is for an element with ANY name. if (typeIndex < 0) { typeIndex = Array.FindIndex(child.ElementNames, name => name.Name == null && name.VersionMatches(version)); } if (typeIndex >= 0) { if (++child.Occurrences > child.Data.MaxCount && child.Data.MaxCount >= 0) { WriteLine($"Element '{parentTree}' has occurred more times than expected."); } IElement elem = ParseElement(child.Types[typeIndex], entry, reader, version, ignoreFlags, parentTree, childIndex, stream); elem.ElementName = elementName; break; } } if (typeIndex < 0) { int i = 0; MultiChildInfo[] infos = multiChildElements.Where(attribInfo => { for (i = 0; i < attribInfo.Attrib.Types.Length; ++i) { ElementName name = attribInfo.ElementNames[i]; if (name.Matches(elementName, version)) { ++attribInfo.Occurrences[i]; return(true); } } return(false); }).ToArray(); if (infos.Length == 0) { //Console.WriteLine("Element '{0}' not supported by parser.", parentTree + elementName + "/"); reader.Skip(); } else { //TODO: verify the multi child type IElement elem = ParseElement(infos[0].Attrib.Types[i], entry, reader, version, ignoreFlags, parentTree, childIndex, stream); elem.ElementName = elementName; } } } } ++childIndex; } if (!entry.WantsManualRead) { ElementName[] underCounted = childElements. Where(x => x.Occurrences < x.Data.MinCount). SelectMany(x => x.ElementNames). Where(x => x.VersionMatches(version)).ToArray(); if (underCounted.Length > 0) { foreach (ElementName elem in underCounted) { WriteLine($"Element '{elem.Name}' has occurred less times than expected."); } } } if (reader.Name == parentElementName) { reader.ReadEndElement(); } else { throw new Exception("Encountered an unexpected node: " + reader.Name); } } } #endregion entry.PostRead(); return(entry); }
private async Task WriteElementAsync( IElement element, XmlWriter writer, CancellationToken cancel) { if (cancel.IsCancellationRequested) { return; } Type elementType = element.GetType(); BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; List <MemberInfo> members = elementType.GetMembers(flags).Where(x => x.GetCustomAttribute <Attr>() != null).ToList(); int xmlnsIndex = members.FindIndex(x => x.GetCustomAttribute <Attr>().AttributeName == "xmlns"); if (xmlnsIndex >= 0) { MemberInfo member = members[xmlnsIndex]; object value = member is PropertyInfo prop?prop.GetValue(element) : (member is FieldInfo field ? field.GetValue(element) : null); members.RemoveAt(xmlnsIndex); await writer.WriteStartElementAsync(null, element.ElementName, value.ToString()); } else { await writer.WriteStartElementAsync(null, element.ElementName, null); } foreach (MemberInfo member in members) { Attr attr = member.GetCustomAttribute <Attr>(); object value = member is PropertyInfo prop?prop.GetValue(element) : (member is FieldInfo field ? field.GetValue(element) : null); if (!attr.Required && value == elementType.GetDefaultValue()) { continue; } await writer.WriteAttributeStringAsync(null, attr.AttributeName, null, value.ToString()); if (cancel.IsCancellationRequested) { return; } } if (element is IStringElement stringEntry) { string value = stringEntry.GenericStringContent.WriteToString(); await writer.WriteStringAsync(value); } else { var orderedChildren = element.ChildElements.Values.SelectMany(x => x).OrderBy(x => x.ElementIndex); foreach (IElement child in orderedChildren) { await WriteElementAsync(child, writer, cancel); } } await writer.WriteEndElementAsync(); }