开发者

Problem in parsing XML file

开发者 https://www.devze.com 2023-02-02 03:01 出处:网络
I have the following XML file: <?xml version=\"1.0\" encoding=\"utf-8\" ?> <strategies> <strategy name=\"God Class\">

I have the following XML file:

<?xml version="1.0" encoding="utf-8" ?>
<strategies>

  <strategy name="God Class">
    <gate type="AND">
      <rule>
        <metric>LOC</metric>
        <comparison>greater than</comparison>
        <value>850</value>
      </rule>
      <rule>
        <metric>FANIN</metric>
        <comparison>greater than</comparison>
        <value>850</value>
      </rule>
      <rule>
        <metric>FANOUT</metric>
        <comparison>greater than</comparison>
        <value>850</value>
      </rule>
    </gate>
  </strategy>

  <strategy name="TClass">
    <gate type="OR">
      <gate type="AND">
        <rule>
          <metric>LOC</metric>
          <comparison>greater than</comparison>
          <value>100</value>
        </rule>
        <rule>
          <metric>NOM</metric>
          <comparison>greater than</comparison>
          <value>200</value>
        </rule>
      </gate>
      <rule>
        <metric>NOPAR</metric>
        <comparison>greater than</comparison>
        <value>300</value>
      </rule>
    </gate>
  </strategy>

</strategies>

Now I try to parse this document and extract the rules. The first strategy is easy with the following code:

public static void parseRules()
        {
            XDocument document = XDocument.Load(FILE);
            XElement root = document.Root;

            foreach (XElement elem in root.Elements())
            { 
                String name = elem.Attribute(STRATEGY_NAME).Value;
                XElement gate = elem.Element(GATE_NAME);

                List<Rule> rules = new List<Rule>();
                foreach (XElement r in gate.Elements())
                {
                        String metric = r.Element(METRIC_NAME).Val开发者_JAVA百科ue;
                        String comparisation = r.Element(COMPARISON_NAME).Value;
                        int threshold = Convert.ToInt32(r.Element(VALUE_NAME).Value);

                        Rule rule = null;

                        if (comparisation.Equals(GREATER_THAN_NAME))
                        {
                            rule = new Rule(metric, Rule.GREATHER_THAN, threshold);
                        }
                        else if (comparisation.Equals(SMALLER_THAN_NAME))
                        {
                            rule = new Rule(metric, Rule.SMALLER_THAN, threshold);
                        }
                        rules.Add(rule);   
                }
                ISpecification spec = rules.ElementAt(0);
                if (gate.Attribute(TYPE_NAME).Value.Equals(AND))
                {
                    for (int i = 1; i < rules.Count; i++)
                    {
                        spec = spec.And(rules.ElementAt(i));
                    }
                }
                else if (gate.Attribute(TYPE_NAME).Value.Equals(OR))
                {
                    for (int i = 1; i < rules.Count; i++)
                    {
                        spec = spec.Or(rules.ElementAt(i));
                    }   
                }
                DetectionStrategy strategy = new DetectionStrategy(spec, name);
            }
        }
    }

Obviously this only works for XML files that have only rules in one gate and not another gate as in the second example. However I'm not able to parse nested gates. Any hints where to start?


Using XDocument or XmlDocument to parse the XML will be very brittle.

I suggest that you design a object model which will cater for this structure and logic and use the XML serialisation/deserialisation to persist or hydrate.

At the end of the day, this XML representation is only one possible representation of your logic, you could have them as JSON, Binary, ...

An exmple:

public class Strategy
{
    [XmlAttribute]
    public string Name {get; set;}

    [XmlElement("Gate")]
    public Gate MainGate {get; get}
}
/// ...........

public class Gate
{

    XmlElement("Gate")
    public Gate ChildGate {get; set;}
    // ...
}    

UPDATE

Here is how you serialise (easy-peasy)

XmlSerializer xs = new XmlSerializer(typeof(Strategy));
FileStream fs = new FileStream("strategy.xml", FileMode.Create);
xs.Serialize(fs, myStrategy);


I would create a Strategies Object that contains a list of Strategy Objects. The Strategy Object would contain a Gate object, etc and then serialize the XML into an instance of those objects. You might run into problems serializing that xml because it is not uniform. You'll have to include a Gates element as a child of the Gate element so that you can include your list of Gate objects...

Example without nested gates...

<Gate>
  <Gates>
  </Gates>
...
</Gate>

Example with nested gates...

<Gate>
  <Gates>
    <Gate>
      <Gates>
      </Gates>
      ...
    </Gate>
  </Gates>
  ...
</Gate>


This problem seems best solved using SAX. There are some implementations of SAX in .NET, although I wish there was a native implementation in .NET.

The problem is you only have two for loops, the most you'll be able to go down is two levels. This can be tedious to keep adding additional for loops. There is a better pattern.

If you weren't to use SAX you need to create a generic engine that can handle any number of gates within gates.

It would be something that goes like this

 public void parseRules (State state, IEnumerable<Element> elements)
 {
      foreach (Element element in elements)
      {
           if (element.Name.Equals("strategy"))
           {
                parseNewStrategy(state, element);
           }
           else if (element.Name.Equals("gate"))
           {
                parseNewGate(state,element);
           }
      }
 }

I am greatly simplifying this. If you want to design this correctly use the Visitor pattern. This is essentially what a SAX engine would do. This will allow you to "visit" each element in the document and take some predefined action whenever you reach the element.

Something like this

If I reach a strategy store its name and set a state field saying I'm in a strategy. Once I hit a gate store information about that gate but don't create a rules array yet. If I hit a new gate push that new operation onto the stack. If I hit a rule create a new rule array and associate it with the top most gate.


If you can access to the XSD definition of the XML file you can use the xsd.exe utility that comes with Visual Studio to auto generate a C# class to process the XML file. With the resulting C# code you will be able to load de XML to a memory structure easily accessible as a collection of items with properties that you can treat as any other collection.

This method also provides a convenient way of writing data to the file using the generated class.

0

精彩评论

暂无评论...
验证码 换一张
取 消