2014-10-29 103 views
2

我有一个相当详细的XML我已经能够解析它的大部分,但即时通过一棵树,只是让我难倒,害怕我正在努力,然后它需要。 这里是我所指的XML。解析XML到列表中

<Codes> 
      <CustomFieldValueSet name="Account" label="Account" distributionType="PercentOfPrice"> 
       <CustomFieldValue distributionValue="10.00" splitindex="0"> 
        <Value>7200</Value> 
        <Description>General Supplies</Description> 
       </CustomFieldValue> 
       <CustomFieldValue distributionValue="45.00" splitindex="1"> 
        <Value>7200</Value> 
        <Description>General Supplies</Description> 
       </CustomFieldValue> 
       <CustomFieldValue distributionValue="45.00" splitindex="2"> 
        <Value>7200</Value> 
        <Description>General Supplies</Description> 
       </CustomFieldValue> 
      </CustomFieldValueSet> 
      <CustomFieldValueSet name="Activity" label="Activity" distributionType="PercentOfPrice" /> 
      <CustomFieldValueSet name="Chart" label="Chart" distributionType="PercentOfPrice"> 
       <CustomFieldValue distributionValue="10.00" splitindex="0"> 
        <Value>T</Value> 
        <Description>University</Description> 
       </CustomFieldValue> 
       <CustomFieldValue distributionValue="45.00" splitindex="1"> 
        <Value>T</Value> 
        <Description>University</Description> 
       </CustomFieldValue> 
       <CustomFieldValue distributionValue="45.00" splitindex="2"> 
        <Value>T</Value> 
        <Description>University</Description> 
       </CustomFieldValue> 
      </CustomFieldValueSet> 
      <CustomFieldValueSet name="Fund" label="Fund" distributionType="PercentOfPrice"> 
       <CustomFieldValue distributionValue="10.00" splitindex="0"> 
        <Value>360806</Value> 
        <Description>National Institutes of Health</Description> 
       </CustomFieldValue> 
       <CustomFieldValue distributionValue="45.00" splitindex="1"> 
        <Value>360903</Value> 
        <Description>National Institutes of Health</Description> 
       </CustomFieldValue> 
       <CustomFieldValue distributionValue="45.00" splitindex="2"> 
        <Value>360957</Value> 
        <Description>National Institutes of Health</Description> 
       </CustomFieldValue> 
      </CustomFieldValueSet> 
      <CustomFieldValueSet name="Program" label="Program" distributionType="PercentOfPrice"> 
       <CustomFieldValue distributionValue="10.00" splitindex="0"> 
        <Value>02</Value> 
        <Description>Research</Description> 
       </CustomFieldValue> 
       <CustomFieldValue distributionValue="45.00" splitindex="1"> 
        <Value>02</Value> 
        <Description>Research</Description> 
       </CustomFieldValue> 
       <CustomFieldValue distributionValue="45.00" splitindex="2"> 
        <Value>02</Value> 
        <Description>Research</Description> 
       </CustomFieldValue> 
      </CustomFieldValueSet> 
      <CustomFieldValueSet name="Location" label="Location" distributionType="PercentOfPrice"> 
       <CustomFieldValue distributionValue="10.00" splitindex="0"> 
        <Value>015</Value> 
        <Description>Biology - Life Science</Description> 
       </CustomFieldValue> 
       <CustomFieldValue distributionValue="45.00" splitindex="1"> 
        <Value>015</Value> 
        <Description>Biology - Life Science</Description> 
       </CustomFieldValue> 
       <CustomFieldValue distributionValue="45.00" splitindex="2"> 
        <Value>015</Value> 
        <Description>Biology - Life Science</Description> 
       </CustomFieldValue> 
      </CustomFieldValueSet> 
      <CustomFieldValueSet name="Organization" label="Organization" distributionType="PercentOfPrice"> 
       <CustomFieldValue distributionValue="10.00" splitindex="0"> 
        <Value>04400</Value> 
        <Description>TUSM:Neuroscience</Description> 
       </CustomFieldValue> 
       <CustomFieldValue distributionValue="45.00" splitindex="1"> 
        <Value>04400</Value> 
        <Description>TUSM:Neuroscience</Description> 
       </CustomFieldValue> 
       <CustomFieldValue distributionValue="45.00" splitindex="2"> 
        <Value>04400</Value> 
        <Description>TUSM:Neuroscience</Description> 
       </CustomFieldValue> 
      </CustomFieldValueSet> 
     </Codes> 

我想结束一个列表将看起来像这样。

Account distributionType Activity distributionValue Fund 
7200  PercentOfPrice  ""  10     360806 
7200  PercentOfPrice  ""  45     360903 
7200  PercentOfPrice  ""  45     360957 

等等

我写的代码看起来是这样的。这是一个片段。介意你我认为我已经完成了这一切。

if (tagName == "Codes") 
           { 
            // Create another reader that contains just the accounting elements. 
            XmlReader inner = reader.ReadSubtree(); 
            //inner.ReadToDescendant("Codes"); 
            //printOutXML(inner); 
            while (inner.Read()) 
            { 
             switch (inner.NodeType) 
             {  
              //walk down the xml hiearchy then simply fill in the values. 
              case XmlNodeType.Element: 

               switch (reader.Name) 
               { 
                case "CustomFieldValueSet": 
                 //get the attribute that we are currently working with such as account and 
                 innerTagName=inner.GetAttribute("name"); 

                 // activity and location can potentially be blank therefore i will check here if it is 
                 //and if it is i will immediate assign the activity list a set of empty quotes. 
                 if (innerTagName == "Activity") 
                 { 
                  if (inner.IsEmptyElement) 
                  { //quickly put fillers in . 
                   for (int i = 0; i < thisInvoice.account.Count; i++) 
                   { 
                    thisInvoice.activity.Add(""); 
                   } 
                  }   
                 } 

                 if (innerTagName == "Location") 
                 { 
                  if (inner.IsEmptyElement) 
                  { //quickly put fillers in . 
                   for (int i = 0; i < thisInvoice.account.Count; i++) 
                   { 
                    thisInvoice.location.Add(""); 
                   } 
                   //thisInvoice.activity.Add(""); 
                  } 
                 } 

                 if (null == inner.GetAttribute("distributionType")) 
                 { 
                  distType = null; 
                 } 
                 else if 
                 (distributionSwitch == false) 
                 { 
                  thisInvoice.distributionType.Add(inner.GetAttribute("distributionType") ?? ""); 
                  distType = inner.GetAttribute("distributionType") ?? ""; 
                 } 
                 //Console.WriteLine(inner.Value); 
                 //Console.WriteLine(inner.Name); 
                 break; 

                case "CustomFieldValue": 
                 if(null == inner.GetAttribute("distributionValue")) 
                 //thisInvoice.distributionValue.Add(inner.GetAttribute("distributionValue") ?? ""); 
                 {/*do nothing*/} 
                else if 
                 (distributionSwitch == false) 
                 { 
                  thisInvoice.distributionValue.Add(inner.GetAttribute("distributionValue") ?? ""); 
                 } 
                 //check the length of the current distribution if the lenght is less than the curren distribution value 
                 // then we must then add the values to the new location. 
                 if (thisInvoice.distributionValue.Count > thisInvoice.distributionType.Count) 
                 { 
                  for (int i = 0; i < thisInvoice.distributionValue.Count - thisInvoice.distributionType.Count; i++) 
                  { 
                   thisInvoice.distributionType.Add(distType); 
                  } 



                 } 

                 break; 

                case "Value": 
                 // XmlNodeType.Text 
                 if (innerTagName == "Account"/*&& inner.NodeType ==XmlNodeType.Text*/) 
                 { 
                  inner.MoveToContent();// move to the text 
                  inner.Read(); 
                  thisInvoice.account.Add(inner.Value); 
                 } 


                 if (innerTagName == "Activity") 
                 { 
                  // activitiy is not a mandartory field so it could be empty therefore we need 
                  // to check if its a self closing tag and if it is then we need to assign and 
                  if (inner.IsEmptyElement) 
                  { 
                   thisInvoice.activity.Add(""); 
                  } 
                  else 
                  { 
                   inner.MoveToContent();// move to the text 
                   inner.Read(); 
                   thisInvoice.activity.Add(inner.Value); 
                  } 
                 } 

                 if (innerTagName == "Location") 
                 { 
                  if (inner.IsEmptyElement) 
                  { 
                   thisInvoice.location.Add(""); 
                  } 
                  else 
                  { 
                   inner.MoveToContent();// move to the text 
                   inner.Read(); 
                   thisInvoice.location.Add(inner.Value); 
                  } 
                 } 

                 if (innerTagName == "Fund") 
                 { 
                  inner.MoveToContent();// move to the text 
                  inner.Read(); 
                  thisInvoice.fund.Add(inner.Value); 
                 } 

                 if (innerTagName == "Organization") 
                 { 
                  inner.MoveToContent();// move to the text 
                  inner.Read(); 
                  thisInvoice.org.Add(inner.Value); 
                 } 

                 if (innerTagName == "Program") 
                 { 
                  inner.MoveToContent();// move to the text 
                  inner.Read(); 
                  thisInvoice.prog.Add(inner.Value); 
                 } 

                 break; 



               }//end switch 
               break;//brake the outside case. 
              case XmlNodeType.EndElement: 
               if (inner.Name == "CustomFieldValueSet" || inner.Value == "CustomFieldValueSet") 
               { 
                distributionSwitch = true; 
                Console.WriteLine(reader.Value); 
                Console.WriteLine(reader.Name); 
               } 
               if (inner.Name == "Codes") 
               { 
                distributionSwitch = false; 
                distType = null; 
                inner.Close(); 
               } 

               break; 
             }//end switch 
            }//end while 
           }//end the if; 

在标签distributionType我需要做的名单长度只要在列表帐户,换句话说,一旦我有它的一个变量,我需要用它作为填充物,使的情况下,分配类型列表与账户列表一样大。 我无法想象有没有更简单的方法来做到这一点我一直在看linq到XML,但它没有多大意义。我很想听听你们中的一些专家如何解决这个问题。我试图用少一点的代码来组合一个优雅的解决方案。 任何帮助将不胜感激。

+0

作为第一个问题,你为什么不走反序列化XML成类,但解析自己的XML的路线? – 2014-10-29 13:18:23

+0

bernd我是一个noob与xml工作。我得到一个XML文件,我打开并处理该文件。最后,我必须创建一个数组列表插入到数据库表中。反序列化的XML可能是最好的选择,但在这一点上,我不知道更好。 – Miguel 2014-10-29 13:22:56

+1

给我一点,我会为你输入一些东西。在此期间看看[本文]中的答案和链接(http://stackoverflow.com/questions/26257041) – 2014-10-29 13:26:20

回答

2

如注释部分所述,Mihai使用LINQ to XML的解决方案的替代方法,您还可以使用预定义的类结构将XML反序列化为类型化的类和属性。

这样做的好处是,你会再有一个对象,它是你的XML的表示(以及希望),让你更容易地与那是XML

内与所提供的XML数据工作样品和使用编辑 - >选择性粘贴 - >粘贴XML作为类在Visual Studio菜单选项,你会得到类似下面的一个类结构(这个已经细化了一下,方便阅读)

using System.Xml.Serialization; 

[XmlTypeAttribute(AnonymousType = true)] 
[XmlRootAttribute(Namespace = "", IsNullable = false)] 
public partial class Codes 
{ 
    [XmlElementAttribute("CustomFieldValueSet")] 
    public List<CodesCustomFieldValueSet> CustomFieldValueSet { get; set; } 
} 

[XmlTypeAttribute(AnonymousType = true)] 
public partial class CodesCustomFieldValueSet 
{ 
    [XmlElementAttribute("CustomFieldValue")] 
    public List<CodesCustomFieldValueSetCustomFieldValue> CustomFieldValue { get; set; } 

    [XmlAttributeAttribute(AttributeName="name")] 
    public string Name { get; set; } 

    [XmlAttributeAttribute(AttributeName = "label")] 
    public string Label { get; set; } 

    [XmlAttributeAttribute(AttributeName = "distributionType")] 
    public string DistributionType { get; set; } 
} 

[XmlTypeAttribute(AnonymousType = true)] 
public partial class CodesCustomFieldValueSetCustomFieldValue 
{ 
    public string Value { get; set; } 

    public string Description { get; set; } 

    [XmlAttributeAttribute(AttributeName = "distributionValue")] 
    public decimal DistributionValue { get; set; } 

    [XmlAttributeAttribute(AttributeName = "splitindex")] 
    public byte SplitIndex { get; set; } 
} 

有了这个类的结构,你就能够用下面的线反序列化XML
(其中txtInput.Text是我用来装样本XML数据的文本框)

XmlSerializer serializer = new XmlSerializer(typeof(Codes)); 
Codes codesInput = serializer.Deserialize(new StringReader(txtInput.Text)) as Codes; 

if (codesInput != null) 
{ 
    // Do something with the data 
} 

注:
从您希望的输出以及您提供的示例XML的结构,将需要您将反序列化对象中的信息转换为您想要的内容/方式,因为我建议创建一个额外的类结构,并将其与List<T>,以保存你所设计的所有信息红色输出。

更妙的是,如果你控制了XML的结构,可以构建它在一个更好的方式,使之更加自明比它目前,它似乎每CustomFieldValueSet之间的联系是splitindex,这是子节点的一个属性,这使得它变得非常复杂。

对XML序列化延伸阅读:
MSDN: Introducing XML Serialization
The XmlSerializer Class

+0

缺少'distributionValue'属性时,你应该用你认为应该采用的任何值替换“0”字符串。我已经测试过它,它很好用。所以,从我+1。 – mihai 2014-10-29 15:23:02

+0

谢谢,它可能会遇到的唯一问题是,将它与LINQ一起使用比直接从XML使用起来要困难得多,但仍然可以使用列表< >,我认为 – 2014-10-29 15:25:21

2

您可以使用Linq to XML

using System.Xml; 
using System.Xml.Linq; 

static void Main(string[] args) { 

// This txt file contains your xml. 
var xml_sample = File.ReadAllText("xml_sample.txt"); 
var doc = XDocument.Parse(xml_sample); 

// Get all <CustomFieldValueSet> that have the label attribute `Account` 
var accounts = from item in doc.Descendants("Codes").Descendants("CustomFieldValueSet") 
       where (item.HasAttributes) && 
        (item.Attribute("label").Value == "Account") 
       select item; 

// Create an anonymous type containing the value of the 
// distributionValue attribute and the <Value> node. 
var accountValue = from el in accounts.Descendants("CustomFieldValue") 
        let distAttribute = el.Attribute("distributionValue") 
        select new 
        { 
         distValue = distAttribute != null ? distAttribute.Value : "0", 
         value = el.Descendants("Value").First().Value, 
        }; 

// Display stuff here just to make sure we got it right. 
accounts.ToList().ForEach(el => 
    Console.WriteLine(el.Name + " " + el.Attribute("distributionType").Value)); 

accountValue.ToList().ForEach(el => 
    Console.WriteLine(el.distValue + ":"+ el.value)); 
} 

您应该能够根据需要使用这些想法来解析您的XML文件。

+0

感谢Mihai我也会给出这个尝试,我很欣赏这个回应。 – Miguel 2014-10-29 13:56:19

+0

Mihai只是一个问题分布类型并不总是XML的一部分,在这种情况下,我会得到一个错误。有没有办法解决这个问题。对不起,我应该在我最初的问题中提到这一点。 – Miguel 2014-10-29 14:10:33

+0

例如,您可以在访问它之前检查属性是否存在:[XML解析检查是否存在属性](http://stackoverflow.com/questions/13342143/xml-parse-check-if-attribute-exist)。我确定存在其他一些方法。 – mihai 2014-10-29 14:13:32