How to create a XML file using XDocument and LINQ in C#? - c#

I have a three List in c# ,the variable names are l_lstData1,l_lstData2,l_lstData3
The File Structure is
<FileDetails>
<Date FileModified="29/04/2010 12:34:02" />
<Data Name="Data_1" DataList="India" Level="2" />
<Data Name="Data_2" DataList="chennai" Level="2" />
<Data Name="Data_3" DataList="hyderabad" Level="2" />
<Data Name="Data_4" DataList="calcutta" Level="2" />
<Data Name="Data_5" DataList="vijayawada" Level="1" />
<Data Name="Data_6" DataList="cochin" Level="1" />
<Data Name="Data_7" DataList="madurai" Level="0" />
<Data Name="Data_8" DataList="trichy" Level="0" />
</FileDetails>
The Values od 3 Lists are as follows :
l_lstData1[0] = "India";l_lstData1[1] = "chennai";l_lstData1[2] = "hyderabad";
l_lstData1[3] = "calcutta";
so the level attribute of the above XML(element : Data) has tha value = "2".
l_lstData2[0] = "vijayawada";l_lstData2[1] = "cochin";
so the level attribute of the above XML(element : Data) has tha value = "1".
l_lstData3[0] = "madurai";l_lstData3[1] = "trichy";
so the level attribute of the above XML(element : Data) has tha value = "0".
How can i create the XML using Xdocument and also using LINQ....Plz revert back me if u have any queries

Here's an alternative to Pramodh's solution, if I've understood it correctly:
// First build up a single list to work with, using an anonymous type
var singleList = l_lstData1.Select(x => new { Value = x, Level = 2})
.Concat(l_lstData2.Select(x => new { Value = x, Level = 1})
.Concat(l_lstData3.Select(x => new { Value = x, Level = 0});
var doc = new XDocument(
new XElement("FileDetails",
new XElement("Date",new XAttribute("FileModified", DateTime.Now)),
singleList.Select((item, index) => new XElement("Data",
new XAttribute("Name", "Data_" + (index + 1)),
new XAttribute("DataList", item.Value),
new XAttribute("Level", item.Level))));

Try like this:
XDocument TEMP = new XDocument(new XElement("FileDetails",
new XElement("Date",new XAttribute("FileModified", DateTime.Now.ToString())),
l_lstData1.Select(l => new XElement("Data",new XAttribute("Name","Data_"+(l_lstData1.IndexOf(l)+1).ToString()),
new XAttribute ("DataList",l.ToString()),
new XAttribute ("Level","Level2"))),
l_lstData2.Select(l => new XElement("Data",new XAttribute("Name","Data_"+(l_lstData2.Count + l_lstData2.IndexOf(l)+1).ToString()),
new XAttribute ("DataList",l.ToString()),
new XAttribute ("Level","Level1"))) ,
l_lstData3.Select(l => new XElement("Data",new XAttribute("Name", "Data_" + (l_lstData3.Count + l_lstData2.Count + l_lstData3.IndexOf(l) + 1).ToString()),
new XAttribute ("DataList",l.ToString()),
new XAttribute ("Level","Level0")))
));
TEMP.Save("TEMP.xml");

Related

How to update XmlNode attribute values in C#

I have an XML file like this:
<caseData>
<entity type="case" name="1">
<attribute name="CASE_OPEN" value="false"/>
<attribute name="CASE_NUMBER" value=""/>
<attribute name="CASE_TYPE" value=""/>
</entity>
<caseData>
I need to update the value for the CASE_NUMBER and CASE_TYPE. The way I only can do is:
_xd = new XmlDocument();
_xd.LoadXml(xmlTemplate);
var caseitem = _xd.GetElementsByTagName("entity")[0];
var childnodes = caseitem.ChildNodes;
foreach (XmlNode node in childnodes)
{
if (node.Attributes["name"].Value == "CASE_NUMBER")
{
node.Attributes["value"].Value = "11222";
}
if (node.Attributes["name"].Value == "CASE_TYPE")
{
node.Attributes["value"].Value = "NEW";
}
}
I am wondering if there is a better way to do it.
Thanks!
Another option is using LINQ to XML. It's generally a nicer API to work with:
var doc = XDocument.Parse(xmlTemplate);
var caseNumber = doc
.Descendants("attribute")
.Single(e => (string)e.Attribute("name") == "CASE_NUMBER");
caseNumber.SetAttributeValue("value", "11222");
If this really is a template and you're just filling in the blanks, you can pretty easily just create it from scratch:
var attributes = new Dictionary<string, string>
{
{"CASE_OPEN", "false"},
{"CASE_NUMBER", "11122"},
{"CASE_TYPE", "NEW"}
};
var caseData = new XElement("caseData",
new XElement("entity",
new XAttribute("type", "case"),
new XAttribute("name", "1"),
AttributeElements(attributes)
)
);
Where AttributeElements is something like:
private static IEnumerable<XElement> AttributeElements(
IReadOnlyDictionary<string, string> attributes)
{
return attributes.Select(x => new XElement("attribute",
new XAttribute("name", x.Key),
new XAttribute("value", x.Value)
));
}

XElement with LINQ add node after specific node

I've got this XML:
<?xml version="1.0" encoding="utf-8"?>
<JMF SenderID="InkZone-Controller" Version="1.2">
<Command ID="cmd.00695" Type="Resource">
<ResourceCmdParams ResourceName="InkZoneProfile" JobID="K_41">
<InkZoneProfile ID="r0013" Class="Parameter" Locked="false" Status="Available" PartIDKeys="SignatureName SheetName Side Separation" DescriptiveName="Schieberwerte von DI" ZoneWidth="32">
<InkZoneProfile SignatureName="SIG1">
<InkZoneProfile Locked="False" SheetName="S1">
<InkZoneProfile Side="Front">
<ColorPool Class="Parameter" DescriptiveName="Colors for the job" Status="Available">
<InkZoneProfile Separation="PANTONE 647 C" ZoneSettingsX="0 0,003 " />
</ColorPool>
</InkZoneProfile>
</InkZoneProfile>
</InkZoneProfile>
</InkZoneProfile>
</ResourceCmdParams>
</Command>
</JMF>
I'm trying to add a node after a specific node() , using XElement and Linq. But my LINQ query always returns me null.
Tried this:
XElement InkZonePath = XmlDoc.Element("JMF").Elements("InkZoneProfile").Where(z => z.Element("InkZoneProfile").Attribute("Side").Value == "Front").SingleOrDefault();
And this:
XmlDoc.Element("JMF")
.Elements("InkZoneProfile").Where(InkZoneProfile => InkZoneProfile.Attribute("Side")
.Value == "Front").FirstOrDefault().AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", x.colorname),
new XAttribute("ZoneSettingsX", x.colorvalues)));
I've built this queries following those examples:
Select XElement where child element has a value
Insert XElements after a specific node
LINQ-to-XML XElement query NULL
But it didn't worked as expected. What is wrong with the LINQ Query ? From what i've read it should work (logically reading the expression i can understand it).
Thanks
EDIT-1: Entire writexml Method
public void writexml(xmldatalist XMLList, variables GlobalVars)
{
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
IndentChars = "\t",
NewLineChars = Environment.NewLine,
NewLineHandling = NewLineHandling.Replace,
Encoding = new UTF8Encoding(false)
};
string DesktopFolder = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
string FileExtension = ".xml";
string PathString = Path.Combine(DesktopFolder, "XML");
System.IO.Directory.CreateDirectory(PathString);
foreach (List<xmldata> i in XMLList.XMLArrayList)
{
int m = 0;
foreach (var x in i)
{
string XMLFilename = System.IO.Path.GetFileNameWithoutExtension(x.xml_filename);
GlobalVars.FullPath = Path.Combine(PathString, XMLFilename + FileExtension);
if (!File.Exists(GlobalVars.FullPath))
{
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("JMF",
new XAttribute("SenderID", "InkZone-Controller"),
new XAttribute("Version", "1.2"),
new XElement("Command",
new XAttribute("ID", "cmd.00695"),
new XAttribute("Type", "Resource"),
new XElement("ResourceCmdParams",
new XAttribute("ResourceName", "InkZoneProfile"),
new XAttribute("JobID", "K_41"),
new XElement("InkZoneProfile",
new XAttribute("ID", "r0013"),
new XAttribute("Class", "Parameter"),
new XAttribute("Locked", "False"),
new XAttribute("Status", "Available"),
new XAttribute("PartIDKeys", "SignatureName SheetName Side Separation"),
new XAttribute("DescriptiveName", "Schieberwerte von DI"),
new XAttribute("ZoneWidth", "32"),
new XElement("InkZoneProfile",
new XAttribute("SignatureName", "SIG1"),
new XElement("InkZoneProfile",
new XAttribute("Locked", "false"),
new XAttribute("SheetName", "S1"),
new XElement("InkZoneProfile",
new XAttribute("Side", "Front"),
new XElement("ColorPoolClass",
new XAttribute("Class", "Parameter"),
new XAttribute("DescriptiveName", "Colors for the job"),
new XAttribute("Status", "Available")
)))))))));
doc.Save(GlobalVars.FullPath);
XDocument XmlDoc = new XDocument();
XmlDoc = XDocument.Load(GlobalVars.FullPath);
XElement InkZonePath = XmlDoc.Root.Descendants("InkZoneProfile").Where(z => (string)z.Attribute("Side") == "Front").SingleOrDefault();
if (InkZonePath != null)
{
InkZonePath.AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", x.colorname),
new XAttribute("ZoneSettingsX", x.colorvalues)));
}
XmlDoc.Save(GlobalVars.FullPath);
}//Closing !FileExists
}//Closing inner foreach
}//Closing outer foreach
}//Closing writexml method
The problem with your current code is here : Element("JMF").Elements("InkZoneProfile") Since InkZoneProfile is not a direct child of JMF it will not return anything. Use Descendants instead.
Check difference between Elements & Descendants.
This should give you correct result:-
XElement InkZonePath = xdoc.Element("JMF").Descendants("InkZoneProfile")
.SingleOrDefault(z => (string)z.Attribute("Side") == "Front")
After this you can add whatever node you want to add using AddAfterSelf. Also note I have used SingleOrDefault here, but you may get exception if you have multiple matching nodes with this, In that case consider using FirstOrDefault.
Update:
To add new node:-
if (InkZonePath != null)
{
InkZonePath.AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", "Test"),
new XAttribute("ZoneSettingsX", "Test2")));
}
//Save XDocument
xdoc.Save(#"C:\Foo.xml");
You need to use Descendants method instead Elements:
XElement InkZonePath = XmlDoc.Root.Descendants("InkZoneProfile").Where(z => (string)z.Attribute("Side") == "Front").SingleOrDefault();
if(InkZonePath !=null)
InkZonePath.AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", x.colorname),
new XAttribute("ZoneSettingsX", x.colorvalues)));
you can use Descendants instead.
var node = XmlDoc.Descendants("InkZoneProfile").Where(x=> x.Attribute("Side") !=null && x.Attribute("Side").Value == "Front").FirstorDefault();

Amending an XML file

I have been trying over the past day to read and amend this xml
<TimeSeriesDocument xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:Test">
<DocumentIdentification v="ZDAE_02417b6a-477a-a-4aa19e1c4712"/>
<TimeSeries>
<Period>
<TimeInterval v="12" />
<Resolution v="PT1H" />
<Interval>
<Pos v="17" />
<Qty v="0" />
</Interval>
<Interval>
<Pos v="19" />
<Qty v="10" />
</Interval>
</Period>
</TimeSeries>
</TimeSeriesDocument>
I want to amend each Qty within each Interval within the period
I have been using Descendants but i'm struggling to loop over each Interval and pick and change the Qty
var doc = XDocument.Load(test.xml);
var periods = doc.Descendants("TimeSeriesDocument/TimeSeries").Select(e => new { name = e.Element("Period").Value });
Can you please help?
A sample console application to loop through Qty nodes and set new value for v attribute:
class Program
{
static void Main(string[] args)
{
var document = new XmlDocument();
document.Load("Sample.xml");
XmlNamespaceManager nsmgr = new XmlNamespaceManager(document.NameTable);
nsmgr.AddNamespace("ns", "urn:Test");
var intervalNodes = document.SelectNodes("//ns:Interval", nsmgr);
foreach(XmlNode node in intervalNodes)
{
var quantityNode = node.SelectSingleNode("./ns:Qty", nsmgr);
quantityNode.Attributes["v"].Value = "New Value";
}
}
}
XNamespace ns = "urn:Test";
XElement doc = XElement.Load("file.xml");
var periods = doc.Descendants(ns + "Period");
var intervals = periods.Elements(ns + "Interval");
foreach (var qty in intervals.Elements(ns + "Qty"))
{
qty.Attribute("v").Value = "42";
}

csv to xml not adding xElement if XAttribute = ""

I've create an xml file from a csv file like this
private void button2_Click(object sender, EventArgs e)
{
String[] FileContent = File.ReadAllLines(csvPathFile);
String XMLNS = "";
int idCountProduct = 1;
XElement Inv = new XElement("data",
from items in FileContent
let fields = items.Split(';')
select new XElement("category",
new XAttribute("idCategory", fields[0]),
new XAttribute("CategoryName", fields[1]),
new XElement("products",
new XElement("product",
new XAttribute("IdProduct", idCountProduct++),
new XAttribute("Rif", fields[0]),
new XAttribute("ProductName", fields[2]),
new XElement("products",
new XElement("product",
new XAttribute("IdProduct", idCountProduct++),
new XAttribute("Rif", fields[0]),
new XAttribute("ProductName", fields[3]),
new XElement("products",
new XElement("product",
new XAttribute("IdProduct", idCountProduct++),
new XAttribute("Rif", fields[0]),
new XAttribute("ProductName", fields[4]));
File.WriteAllText(xmlPathFile, XMLNS + Inv.ToString());
}
this is my csv file
1;Beverages;Lemon Juice;;Orange Juice
this is the xml file i want to create
<data>
<category idCategory="1" CategoryName= "Beverages">
<products>
<product IdProduct="1" Rif="1" ProductName= "Lemon Juice" />
<product IdProduct="2" Rif="1" ProductName= "Orange Juice" />
<products/>
<category/>
</data>
and this is the xml file i obtain
<data>
<categories>
<category idCategory="1" CategoryName= "Beverages">
<products>
<product IdProduct="1" Rif="1" ProductName= "Lemon Juice" />
<product IdProduct="2" Rif="1" ProductName= "" />
<product IdProduct="3" Rif="1" ProductName= "Orange Juice" />
<products/>
<category/>
<categories/>
</data>
How can I avoid adding a product if ProductName is not assigned?
Add a filter to your LINQ query to filter out empty products:
where !String.IsNullOrEmpty(fields[4])
just after this:
let fields = items.Split(';')
Anyway if your condition is so simple you do not even need it and you may filter out entries from the Split instruction itself:
let fields = items.Split(';', StringSplitOptions.RemoveEmptyEntries)
EDIT
Your code is pretty fixed so...are you sure you need to use LINQ and LINQ-to-XML? I guess it'll be even more readable anyway...write this:
XElement data = new XElement("data");
XDocument document = new XDocument(data);
int counter = 0;
foreach (string entry in File.ReadAllLines(csvPath))
{
string[] fields = entry.Split(new char[] { ';' },
StringSplitOptions.RemoveEmptyEntries);
XElement category = new XElement("category",
new XAttribute("idCategory", fields[0]),
new XAttribute("CategoryName", fields[1]));
data.Add(category);
XElement products = new XElement("products");
category.Add(products);
for (int i = 2; i < fields.Length; ++i)
{
products.Add(new XElement("product",
new XAttribute("IdProduct", counter++),
new XAttribute("Rif", fields[0]),
new XAttribute("ProductName", fields[i])));
}
}
document.Save(xmlPath);
}

C# : Reading the XML using XDocument and LINQ

The Input XML Format is
<FileDetails>
<Date FileModified="28/06/2010 10:43:36" />
<Data Name="DIG" List="U16,R30" Level="2"/>
<Data Name="DIG1" List="Uee,Ree" Level="2"/>
<Data Name="DIG2" List="Udd,Rdd" Level="2"/>
<Data Name="N234" List="J3" Level="2"/>
<Data Name="N11" List="U2" Level="1"/>
<Data Name="N12" List="U219" Level="1"/>
<Data Name="N13" List="U218" Level="1"/>
<Data Name="N14" List="U243" Level="1"/>
<Data Name="N15" List="U142" Level="0"/>
<Data Name="N16" List="U119" Level="0"/>
<Data Name="N17" List="U118" Level="0"/>
<Data Name="N18" List="U143" Level="0"/>
</FileDetails>
Read the above XML based Up on the Attribute : "Level".
Data Structure:
Dictionary<int,string> l_dicttLevel1 = new Dictionary<int,string>();
Dictionary<int,List<string>> l_dicttLevel2 = new Dictionary<int,List<string>>();
Dictionary<int,string> l_dicttLevel0 = new Dictionary<int,string>();
Output:
For l_dicttLevel1 :
l_dictLevel1[1] = "U2"
l_dictLevel1[2] = "U219"
l_dictLevel1[3] = "U218"
l_dictLevel1[4] = "U243"
For l_dicttLevel0 :
l_dictLevel0[1] = "U142"
l_dictLevel0[2] = "U119"
l_dictLevel0[3] = "U118"
l_dictLevel0[4] = "U143"
For l_dicttLevel2 :
Here i ll seperate the values(i.e)List<string> of Dictionary(l_dicttLevel2 ) by using Comma.
l_dictLevel2[1] = "U16","R30"
l_dictLevel2[2] = "Uee","Ree"
l_dictLevel2[3] = "Udd","Rdd"
Here is my Code :
XmlDocument xDoc = new XmlDocument();
xDoc.Load(l_strPath);
XmlElement Root = xDoc.DocumentElement;
int l_nCount = 0;
l_dicttLevel1 = (from XmlNode l_nNode in Root.SelectNodes("//Data")
where l_nNode.Attributes["Level"].Value == "1"
select new
{
Key = l_nCount++,
Value = l_nNode.Attributes["List"].Value
}).ToDictionary(l_strTemp => Convert.ToInt32(l_strTemp.Key), l_strTemp => l_strTemp.Value);
l_dicttLevel2 = (from XmlNode l_nNode in Root.SelectNodes("//Data")
where l_nNode.Attributes["Level"].Value == "0"
select new
{
Key = l_nCount++,
Value = l_nNode.Attributes["List"].Value
}).ToDictionary(l_strTemp => Convert.ToInt32(l_strTemp.Key), l_strTemp => l_strTemp.Value);
l_dicttLevel2= (from XmlNode l_nNode in Root.SelectNodes("//Data")
where l_nNode.Attributes["Level"].Value == "2"
select new
{
Key = l_nCount++,
Value = l_nNode.Attributes["List"].Value
}).ToDictionary(l_strTemp => Convert.ToInt32(l_strTemp.Key),
l_strTemp => l_strTemp.Value.Split(',').ToList());
xDoc = null;
Is it possible Using LINQ? . Please let me know if u have any queries.
Try this
XDocument XDOC = XDocument.Load(Application.StartupPath + "\\Test.xml");
dictLevel1 = XDOC.Descendants("Data").Where(x => (Int32)x.Attribute("Level") == 1)
.Select((a, b) => new { Index=b, Element=a})
.ToDictionary(x => x.Index+1,x=>x.Element.Attribute("List").Value );
dictLevel0 = XDOC.Descendants("Data").Where(x => (Int32)x.Attribute("Level") == 0)
.Select((a, b) => new { Index = b, Element = a })
.ToDictionary(x => x.Index + 1, x => x.Element.Attribute("List").Value);
dictLevel2 = XDOC.Descendants("Data").Where(x => (Int32)x.Attribute("Level") == 2)
.Select((a, b) => new { Index = b, Element = a })
.ToDictionary(x => x.Index + 1, x => x.Element.Attribute("List").Value.Split(',').ToList());

Categories

Resources