首页 > 解决方案 > 如何从xml获取接口类型的对象列表

问题描述

我如何List<IntrfaceType>从 xml 中获取:

<?xml version="1.0" encoding="utf-8" ?>
 <processSettings>
   <processName>chrome.exe</processName>
   <location>L1</location>
   <watch>true</watch>
   <rules>
        <autoStart></autoStart>
        <noMore></noMore>
        <unExpectedCrush></unExpectedCrush>
    </rules>
 </processSettings>

我试过这样:

[XmlRootAttribute("rules", Namespace = "", IsNullable = false)]
public class Rules
{
    [XmlArrayItem("autoStart", Type = typeof(AutoStart)),
     XmlArrayItem("noMore", Type = typeof(NoMore)),
     XmlArrayItem("unExpectedCrush", Type = typeof(UnExpectedCrush))]
    public Rule[] RuleItems;

XmlSerializer ruleSerializer = new XmlSerializer(typeof(Rule[]), new XmlRootAttribute("rules"));
        Rule[] rules = (Rule[])ruleSerializer.Deserialize(config.Rules.CreateReader());

在这种情况下Rule : IRuleAutoStart, NoMore, UnExpectedCrush : Rule我想得到 Rule[]然后转换它List<IRule>,但Deserialize返回一个空数组。所以请帮助理解我做错了什么。

标签: c#xml-serialization

解决方案


好的,所以我认为您正在尝试做的是将所有内容映射到:

   <rules>
        <autoStart></autoStart>
        <noMore></noMore>
        <unExpectedCrush></unExpectedCrush>
    </rules>

到规则列表。而且您只是尝试直接在 XML 上运行反序列化,并假设它将神奇地将子级映射rulesIRule.

如果你真的想要一个规则列表,你首先必须以某种方式提取信息。这最容易通过将 XML 映射到类型化对象来完成。因此,在您的情况下,我不会以列表结尾,而是创建一个映射对象:

public class Rules
{
    public string AutoStart { get; set; }
    public string NoMore { get; set; }
    public string UnExpectedCrush { get; set; }
}

所以做类似的事情:

(Rules) ruleSerializer.Deserialize(config.Rules.CreateReader());

现在您有一个对象,其中映射了您的所有规则。从那时起,您可以将属性重新映射到规则列表中。但我认为拥有一个实际的 Rules 对象,其中规则被命名,对于可能阅读您的代码的其他人来说通常更清楚。那只是我的两分钱。

或者:如果您可以控制 XML,则可以执行以下操作:

 <processSettings>
   <processName>chrome.exe</processName>
   <location>L1</location>
   <watch>true</watch>
   <rules>
        <rule>autoStart</rule>
        <rule>noMore</rule>
        <rule>unExpectedCrush</rule>
    </rules>
 </processSettings>

并将其反序列化为规则列表:

(List<Rule>)deserializer.Deserialize(config.Rules.CreateReader());

然后你有你的规则列表,可以翻译成它的接口版本。

但这会更改 XML,因此它假定您可以控制它。

编辑: List<Rule>List<IRule>

因此,如果您能够获得列表List<Rule>并且 Rule 具有相关接口。然后你可以改变你的List<Rule>

List<Rule> rules = new List<Rule>();
List<IRule> iRules = rules.ToList<IRule>();

取自这个答案StackOverflow 答案

EDIT2:将 XElement 序列化为规则列表:

好的,所以我仍然有点难以理解为什么需要将这些规则映射到对象而不是具有值的规则。我会说一个规则列表,其中包含一个字符串,说明哪种规则将使您的生活更轻松。然后你只需要进行字符串比较,看看你有什么规则。

因此,我认为这将解决您的问题:

public List<IRule> GetListOfRules()
{
    var rulesElement = XElement.Parse(@"<rules>
        <autoStart></autoStart>
        <noMore></noMore>
        <unExpectedCrush></unExpectedCrush>
    </rules>");

    var listOfRulesFromElement = 
        rulesElement.Elements().Select(d => new Rule { RuleName = d.Name.LocalName }).ToList<IRule>();

    return listOfRulesFromElement;
}

public class Rule : IRule
{
    public string RuleName { get; set; }
    public string GetRule()
    {
        return RuleName;
    }
}

public interface IRule
{
    string GetRule();
}

使用此代码,您的结构和规则之间没有任何硬耦合。您只需将新规则添加到您的 XML 元素,它就会自动添加到您的规则列表中。然后您可以检查规则列表并根据RuleName. 我希望这是你想要的。

编辑 3:类型化映射:

如果您需要类型化映射,我建议使用枚举:

public List<IRule> GetListOfRules()
{
    var rulesElement = XElement.Parse(@"<rules>
        <autoStart></autoStart>
        <noMore></noMore>
        <unExpectedCrush></unExpectedCrush>
    </rules>");

    var listOfRulesFromElement = 
        rulesElement.Elements().Select(d =>
        {
            var parsed = Enum.TryParse(d.Name.LocalName, out RuleType ruleName);
            if (!parsed)
                ruleName = RuleType.UnmappedRule;
            return new Rule {RuleName = ruleName};
        }).ToList<IRule>();

    return listOfRulesFromElement;
}

public class Rule : IRule
{
    public RuleType RuleName { get; set; }
    public RuleType GetRule()
    {
        return RuleName;
    }
}

public enum RuleType
{
    UnmappedRule,
    AutoStart,
    NoMore,
    UnExpectedCrush
}

public interface IRule
{
    RuleType GetRule();
}

推荐阅读