解析XML文件时出现问题

时间:2010-12-31 16:56:05

标签: c# .net wpf xml

我有以下XML文件:

<?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>

现在我尝试解析此文档并提取规则。使用以下代码可以轻松实现第一个策略:

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).Value;
                        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);
            }
        }
    }

显然,这仅适用于在第二个示例中仅在一个门中而不是另一个门中的规则的XML文件。但是我无法解析嵌套的门。任何提示从哪里开始?

4 个答案:

答案 0 :(得分:4)

使用XDocument或XmlDocument来解析XML将非常脆弱。

我建议你设计一个对象模型来满足这个结构和逻辑,并使用 XML序列化/反序列化来保持或保持水分。

在一天结束时,这个XML表示只是您逻辑的一种可能表示,您可以将它们作为JSON,Binary,......

一个例子:

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;}
    // ...
}    

<强>更新

以下是序列化(easy-peasy)的方法

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

答案 1 :(得分:1)

我会创建一个包含策略对象列表的策略对象。策略对象将包含Gate对象等,然后将XML序列化为这些对象的实例。您可能会遇到序列化该xml的问题,因为它不一致。您必须将Gates元素包含为Gate元素的子元素,以便包含Gate对象列表...

没有嵌套门的示例......

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

嵌套门的例子......

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

答案 2 :(得分:1)

使用SAX似乎可以最好地解决这个问题。在.NET中有一些SAX的实现,尽管我希望在.NET中有一个本机实现。

问题是你只有两个for循环,你可以下去的最多是两个级别。继续添加额外的for循环可能很繁琐。有一个更好的模式。

如果您不使用SAX,则需要创建一个可以处理门内任意数量门的通用引擎。

这将是这样的事情

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

我正在大大简化这一点。如果要正确设计,请使用Visitor模式。这基本上就是SAX引擎的功能。这将允许您“访问”文档中的每个元素,并在到达元素时执行一些预定义的操作。

像这样的东西

如果我到达战略商店的名称并设置一个状态字段,说我在策略中。一旦我点击门存储有关该门的信息但尚未创建规则数组。如果我按下新的门将新操作推到堆栈上。如果我点击规则,则创建一个新规则数组并将其与最顶端的门关联。

答案 3 :(得分:1)

如果可以访问XML文件的XSD定义,则可以使用Visual Studio附带的xsd.exe实用程序自动生成C#类来处理XML文件。使用生成的C#代码,您将能够将de XML加载到一个内存结构中,该结构可以作为一组项目轻松访问,这些项目具有您可以视为任何其他集合的属性。

此方法还提供了使用生成的类将数据写入文件的便捷方式。