2014-10-29 105 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