Friday, December 19, 2008

XML manupilation(2) --Linq to XML

Let's use the sample xml file in msdn:

Our goal is to convert this xml file to pipeline delimited txt file based on the node name. We save the xml in C:\sample.xml

We can write code like:

static void Main(string[] args)
{
XElement el = XElement.Load(@"C:\sample.xml");
var query = from c in el.Descendants("Name")
select new
{
TOTAL_TIME_UNITS = (string)c.Parent.Element("TIME").Element("TOTAL_TIME").Attribute("units"),
TOTAL_TIME = (string)c.Parent.Element("TIME").Element("TOTAL_TIME"),

SpecNumber= c.Element("Spec") != null ?(c.Element("Spec").Element("SpecNumber")!=null?(string)c.Element("Spec").Element("SpecNumber"):String.Empty):String.Empty,

SpecElevationImage1 = c.Element("Spec") != null ? (c.Element("Spec").Element("SpecImages") != null ? ((from si in c.Element("Spec").Element("SpecImages").Elements("SpecElevationImage") select si).ToArray().Length > 0 ? (string)(from si in c.Element("Spec").Element("SpecImages").Elements("SpecElevationImage") select si).ToArray()[0] : String.Empty) : String.Empty) : String.Empty,

SpecElevationImage2 = c.Element("Spec") != null ? (c.Element("Spec").Element("SpecImages") != null ? ((from si in c.Element("Spec").Element("SpecImages").Elements("SpecElevationImage") select si).ToArray().Length > 1 ? (string)(from si in c.Element("Spec").Element("SpecImages").Elements("SpecElevationImage") select si).ToArray()[1] : String.Empty) : String.Empty) : String.Empty,

TagI=(from tg in c.Parent.Element("TAGS").Elements("TAG") select tg).ToArray()
}
FileStream file = new FileStream(@"C\result.txt", FileMode.OpenOrCreate, FileAccess.Write);
StreamWriter sw = new StreamWriter(file);

foreach (var item in query)
{

string[, ,] Tag = new string[9, 9, 10];
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
for (int k = 0; k < 10; k++)
{
Tag[i,j,k]="";
}

for (int i = 0; i < item.TagI.Length; i++)
{
Tag[i+1,0,0]=(string)((item.TagI)[i].Element("TAG_NAME"));
if ((item.TagI)[i].Element("SUBTAGS") != null)
{
var TagJquery = from c in (item.TagI)[i].Element("SUBTAGS").Descendants("TAG")
select new
{
TagNameJ = (string)c.Element("TAG_NAME"),
TagK = c.Element("SUBTAGS")!=null?(from tg in c.Element("SUBTAGS").Elements("TAG") select tg).ToArray():null

};

for(int j=0;j {
Tag[i+1, j+1, 0] = TagJquery.ToArray()[j].TagNameJ;
if (TagJquery.ToArray()[j].TagK != null)
{
for (int k = 0; k < TagJquery.ToArray()[j].TagK.Length; k++)
{
Tag[i+1, j+1, k+1] = (string)(TagJquery.ToArray()[j].TagK)[k].Element("TAG_NAME");
}
}
}
}
}

sw.WriteLine("{0}|{1}|{2}|{3}|{4}|{5}|{6}|{7}|{8}",item.TOTAL_TIME_UNITS, item.TOTAL_TIME_UNITS,item.SpecNumber, item.SpecElevationImage1, item.SpecElevationImage2,Tag[2,3,4],Tag[1,6,3],Tag[3,1,0]);
sw.Close();
file.Close();
}

The above code gives example how to query xml element and attribute using Linq to XML, notice that the element names or attribute names in the quotes are case sensitive.

This is the following point to notice:
1. The attribute in one node can be more than 1.
2. If one node does not exist, we can use ...Element("nodeA")!=null?(sting)....:String.Empty
3. If in the same node there are mulitple Elements with same name, we can use (from ...select ) query, and there are method ToArray() to convert this query to array, and when using index, it goes to the specific element.
4. If there are node under node, the structure like:

Tags-Tag-TagName
-Tag-TagName
-SubTags-Tag-TagName
-Tag-TagName
-SubTag-Tag-TagName
We can use foreach or for iterator to get the contents out.



No comments: