C#从XML高效提取信息并复用的优化方案咨询
嘿,作为C#和OOP新手能自己折腾XML读取和对象映射已经很棒了!你的当前实现确实能跑,但扩展性差是硬伤——每次加新属性都要改Character类的构造函数、CreateCharacterFromXML里的索引取值,太麻烦。咱们一步步来优化,先从简化XML读取开始,再搞定无Schema的XML反序列化,最后给你一些可扩展的设计建议。
一、先优化现有XML读取逻辑(过渡方案)
你当前用List<CharacterAttributes>存属性,然后靠索引取值的方式极易出错(比如XML里属性顺序变了就炸)。不如改成Dictionary<string, int>,用属性名当键,摆脱对顺序的依赖:
修改ReadAndLoad的FetchAttributes方法
public Dictionary<string, int> FetchAttributes(string userPath) { XmlDocument XmlDoc = new XmlDocument(); try { Console.WriteLine("\nNow Loading: {0}\n", userPath); XmlDoc.Load(userPath); } catch (System.IO.FileNotFoundException) { Console.WriteLine("No file found!"); Environment.Exit(1); } catch (System.ArgumentException) { Console.WriteLine("Invalid path detected!"); Environment.Exit(1); } catch (Exception err) { Console.WriteLine("An Exception has been caught:"); Console.WriteLine(err); Environment.Exit(1); } XmlNodeList XmlDocNodes = XmlDoc.SelectNodes("/character/attributes/attribute"); var attributeDict = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); // 忽略大小写提升容错 foreach (XmlNode node in XmlDocNodes) { string name = node["name"].InnerText.Trim(); if (int.TryParse(node["totalvalue"].InnerText, out int value)) { attributeDict[name] = value; Console.WriteLine($"{name}: {value}"); } else { Console.WriteLine($"Warning: Could not parse value for attribute '{name}'"); } } return attributeDict; }
修改CreateCharacterFromXML方法
public Character CreateCharacterFromXML(string userPath) { var readAndLoad = new ReadAndLoad(); var attributeDict = readAndLoad.FetchAttributes(userPath); // 用GetValueOrDefault处理缺失属性,给默认值避免崩溃 int bod = attributeDict.GetValueOrDefault("Body", 0); int agi = attributeDict.GetValueOrDefault("Agility", 0); int rea = attributeDict.GetValueOrDefault("Reaction", 0); int str = attributeDict.GetValueOrDefault("Strength", 0); int cha = attributeDict.GetValueOrDefault("Charisma", 0); int intuition = attributeDict.GetValueOrDefault("Intuition", 0); int log = attributeDict.GetValueOrDefault("Logic", 0); int wil = attributeDict.GetValueOrDefault("Willpower", 0); int edg = attributeDict.GetValueOrDefault("Edge", 0); int mag = attributeDict.GetValueOrDefault("Magic", 0); int res = attributeDict.GetValueOrDefault("Resonance", 0); int ess = attributeDict.GetValueOrDefault("Essence", 0); return new Character(bod, agi, rea, str, intuition, cha, log, wil, edg, mag, res, ess); }
这样即使XML属性顺序调整,或者新增属性,你只需要在Character类和构造函数里加对应字段,不用改索引,容错性和可维护性都提升了。
二、搞定无Schema的XML反序列化(终极优雅方案)
你之前反序列化失败,大概率是没正确配置类的XML特性——完全不用管Schema,用XmlSerializer就能直接映射。假设你的XML结构是这样的:
<character> <attributes> <attribute> <name>Body</name> <totalvalue>6</totalvalue> </attribute> <attribute> <name>Agility</name> <totalvalue>5</totalvalue> </attribute> <!-- 其他属性 --> </attributes> </character>
定义对应XML结构的类
// 对应单个<attribute>节点 [XmlRoot("attribute")] public class AttributeItem { [XmlElement("name")] public string Name { get; set; } [XmlElement("totalvalue")] public int TotalValue { get; set; } } // 对应整个XML根节点 [XmlRoot("character")] public class CharacterAttributesRoot { [XmlArray("attributes")] [XmlArrayItem("attribute")] public List<AttributeItem> Attributes { get; set; } }
写反序列化方法
public class XmlDeserializer { public CharacterAttributesRoot DeserializeCharacter(string filePath) { try { using (var reader = new StreamReader(filePath)) { var serializer = new XmlSerializer(typeof(CharacterAttributesRoot)); return (CharacterAttributesRoot)serializer.Deserialize(reader); } } catch (System.IO.FileNotFoundException) { Console.WriteLine("No file found!"); Environment.Exit(1); } catch (Exception err) { Console.WriteLine("An Exception has been caught:"); Console.WriteLine(err); Environment.Exit(1); } return null; } }
用反序列化结果创建Character对象
public Character CreateCharacterFromXML(string userPath) { var deserializer = new XmlDeserializer(); var root = deserializer.DeserializeCharacter(userPath); var attributeDict = root.Attributes.ToDictionary( attr => attr.Name, attr => attr.TotalValue, StringComparer.OrdinalIgnoreCase); return new Character( attributeDict.GetValueOrDefault("Body", 0), attributeDict.GetValueOrDefault("Agility", 0), attributeDict.GetValueOrDefault("Reaction", 0), attributeDict.GetValueOrDefault("Strength", 0), attributeDict.GetValueOrDefault("Intuition", 0), attributeDict.GetValueOrDefault("Charisma", 0), attributeDict.GetValueOrDefault("Logic", 0), attributeDict.GetValueOrDefault("Willpower", 0), attributeDict.GetValueOrDefault("Edge", 0), attributeDict.GetValueOrDefault("Magic", 0), attributeDict.GetValueOrDefault("Resonance", 0), attributeDict.GetValueOrDefault("Essence", 0)); }
这个方案的优势是:XML读取逻辑完全交给框架处理,你只需要维护类和XML节点的映射关系,新增属性时只需要在Character类和构造函数里加对应字段即可。
三、进阶可扩展设计建议
如果想进一步提升扩展性,甚至不用每次修改Character类,可以试试:
- 用字典存储属性:把
Character改成一个Dictionary<string, int>,新增属性完全不用改类,但会失去强类型的编译检查优势。 - 反射赋值:定义XML属性名和
Character属性名的映射配置,用反射自动把字典值赋值给Character对象,连构造函数都不用改(适合属性极多的场景)。
不过作为新手,先把前面的反序列化+字典过渡方案搞懂就足够了,反射可以后续再深入学习。
内容的提问来源于stack exchange,提问作者Joschi
相关产品推荐
相关产品推荐

