简体   繁体   中英

How to get a list of objects which are interface type from xml

How can I get List<IntrfaceType> from 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>

I've tried like this:

[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());

In this case Rule : IRule and AutoStart, NoMore, UnExpectedCrush : Rule , so I want to get Rule[] then transform it List<IRule> , but Deserialize returns an empty array. So please help to understand what I`m doing wrong.

Ok so what I think you are trying to do is map everything in:

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

to a list of rules. And you are just trying to run a deserialise directly on the XML and making the assumption it will magically map the children of rules into IRule .

If you really want a list of rules, you first have to somehow extract the information. This is most easily done by mapping the XML into a typed object. So in your case instead of ending up with a list I would make a mapping object:

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

So do something like:

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

Now you have an object that has all your rules mapped out in it. And from then on you can remap the properties into a list of rules. But I think that having an actual Rules object, where the rules are named, often is more clear to other people that might read your code. That is just my two cents.

Alternatively: If you have control over the XML you can do something like:

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

and deserialize it into a list of rules:

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

And then you have your list of rules which can be translated into the interface version of it.

But that changes the XML so it assumes you have control over that.

Edit: List<Rule> to List<IRule>

So if you are able to get a list of List<Rule> and Rule has the interface in question. Then you can transform your List<Rule> :

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

Taken from this answer StackOverflow answer

EDIT2: Serializing XElement into list of Rules:

Ok so I still have a bit of a hard time to understand why you need to map these rules into an object instead of a rule with a value. I would say a list of rules that contain a string that says which kind of rule it is will make your life easier. Then you just have to make string comparison to see what rules you have.

So with that I think this will solve your problem:

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();
}

With this code you don't have any hard coupling between your structure and the rules. You can just add new rules to your XML element and it will automatically be added to your list of rules. Then you can inspect the list of rules and see what rules are there based on the RuleName . I hope this is what you want.

Edit 3 : Typed mapping:

If you need a typed mapping I suggest an enum:

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();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM