2010-08-18 60 views
1

我想解析C#中网站的XML响应。响应的格式类似于以下内容:在C#XML中解析特定内容的XML

<Company> 
    <Owner>Bob</Owner> 
    <Contact> 
     <address> -1 Infinite Loop </address> 
     <phone> 
      <LandLine>(000) 555-5555</LandLine> 
      <Fax> (000) 555-5556 </Fax> 
     </phone> 
     <email> [email protected] </email> 
    </Contact> 
</Company> 

我想要的唯一信息是LandLine和传真号码。然而,我目前的做法似乎真的很差质量。本质上它是一堆嵌套的while循环,并检查元素名称,然后在找到正确的元素时读取内容。我正在使用类似下面的清单:

XmlReader xml = XmlReader.Create(websiteResultStream, xmlSettings); 

while(xml.Read()){ 
    if(xml.NodeType == XmlNodeType.Element){ 
     if(xml.Name.ToString() == "Phone"){ 
      while(xml.Read()) { 
       if(xml.NodeType == XmlNodeType.Element) { 
        if(xml.Name.ToString() == "LandLine"){ 
          xml.MoveToContent(); 
          xml.ReadContentAsString(); 
        } 
        if(xml.Name.ToString() == "Fax"){ 
          xml.MoveToContent(); 
          xml.ReadContentAsString(); 
        } 
       } 
      } 
     } 
    } 
} 

我是较新的XML/C#,但上述方法只是尖叫坏代码!我想确保如果结构发生变化(即,有另外的电话号码类型,如“移动”),代码是健壮的(因此,额外的while循环)

注意:上述C#代码不准确,缺乏一些检查等,但它展示了我目前令人厌恶的方法

如果简单地从这两个元素中提取内容,如果它们存在,最好/最干净的方法是什么?

回答

8

进行只读访问特定节点的XML文档中的最轻质的方法是通过使用XPathDocument以及XPath表达式:

XPathDocument xdoc = new XPathDocument(@"C:\sample\document.xml"); 
XPathNavigator node = xdoc.CreateNavigator() 
    .SelectSingleNode("/Company/Contact/phone/LandLine"); 
if (node != null) 
{ 
    string landline = node.Value; 
} 
8

var doc = XDocument.Parse(@"<Company> 
    <Owner>Bob</Owner> 
    <Contact> 
     <address> -1 Infinite Loop </address> 
     <phone> 
      <LandLine>(000) 555-5555</LandLine> 
      <Fax> (000) 555-5556 </Fax> 
     </phone> 
     <email> [email protected] </email> 
    </Contact> 
</Company>"); 

var phone = doc.Root.Element("Contact").Element("phone"); 

Console.WriteLine((string)phone.Element("LandLine")); 
Console.WriteLine((string)phone.Element("Fax")); 

输出:

 
(000) 555-5555 
(000) 555-5556 
+3

请注意,如果联系人丢失,您将在'var phone = ...'行中收到异常。我喜欢做'var contactNode = doc.Root.Element(“Contact”)之类的东西?新的XElement(“Contact”);'所以我总是有一个节点返回,然后当我做'var phone = contact.Element(“phone”)?新的XElement(“电话”);'我不会得到null对象错误。最后,我最终得出了变量的空白值。或者在解析之前使用xsd来验证文档,以确保您想要的节点存在。 – CaffGeek 2010-08-18 14:48:56

+3

请注意,'XDocument'类还带有在内存中构建DOM树的开销;通常不是您需要对文档中的节点进行只读随机访问,特别是在处理大型文档时。 – 2010-08-18 15:12:08

2

我不认为你太遥远了。有更方便的方法(很多不同的方法)。假设你想,你在这里做采取相同的基本方法(这是一种有效的,如果详细的一个),我会做:

bool inPhone = false; 
string landLine = null; 
string fax = null; 

using(xml = XmlReader.Create(websiteResultStream, xmlSettings) 
while(xml.Read()) 
{ 
    switch(xml.NodeType) 
    { 
    case XmlNodeType.Element: 
     switch(xml.LocalName) 
     { 
     case "phone": 
      inPhone = true; 
      break; 
     case "LandLine": 
      if(inPhone) 
      { 
      landLine = xml.ReadElementContentAsString(); 
      if(fax != null) 
      { 
       DoWhatWeWantToDoWithTheseValues(landline, fax); 
       return; 
      } 
      } 
      break; 
     case "Fax": 
      if(inPhone) 
      { 
      fax = xml.ReadElementContentAsString(); 
      if(landLine != null) 
      { 
       DoWhatWeWantToDoWithTheseValues(landline, fax); 
       return; 
      } 
      } 
      break; 
     } 
     break; 
    case XmlNodeType.EndElement: 
     if(xml.LocalName == "phone") 
     inPhone = false; 
     break; 
    } 
} 

请注意,此跟踪它是否是“内部” phone元素如该您将重新检查后面元素中的LandLine,这似乎是您试图避免的。

另请注意,我们清理XmlReader,并在我们获得所有我们想要的信息后立即返回。