How to make this xml query shorter - c#

I spent 3 days to read this xml file and put the details in to the database. It works the way it should be but I know the way i read this xml file is not the proper way.
If the xml file is bigger than 2mb. (which contains about 1000 records), it takes more than 1 minute to load.
Can you please show me how to make this query shorter.
this is the xml
<?xml version="1.0" encoding="UTF-8"?>
<outputTree xmlns="http://www.ibm.com/software/analytics/spss/xml/oms" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ibm.com/software/analytics/spss/xml/oms http://www.ibm.com/software/analytics/spss/xml/oms/spss-output-1.8.xsd">
<command command="Summarize" displayOutlineValues="label" displayOutlineVariables="label" displayTableValues="label" displayTableVariables="label" lang="en" text="Summarize">
<pivotTable subType="Report" text="Batch">
<dimension axis="row" text="Cases">
<group label="Test Site" text="Test Site" varName="PLANT_DESC" variable="true">
<group hide="true" text="A">
<group string="A" text="A" varName="PLANT_DESC">
<group label="Product" text="Product" varName="PROD_DESC" variable="true">
<group hide="true" text="A">
<group string="S" text="S" varName="PROD_DESC">
<group label="Batch Number" text="Batch Number" varName="BATCH_NO" variable="true">
<group hide="true" text="A">
<group number="3704542" text="3704542" varName="BATCH_NO">
<category number="1" text="1">
<dimension axis="column" text="Variables">
<category label="Batch Run" text="Batch Run" varName="BATCH_RUN_ID" variable="true">
<cell number="4202" text="4202" varName="BATCH_RUN_ID"/>
</category>
<category label="Application" text="Application" varName="APP_ID" variable="true">
<cell label="Calibration" number="101" text="Calibration" varName="APP_ID"/>
</category>
<category label="Date Tested" text="Date Tested" varName="TEST_DATE" variable="true">
<cell date="2014-09-23T10:53:19" format="date" text="23-SEP-2014" varName="TEST_DATE"/>
</category>
</dimension>
</category>
</group>
</group>
</group>
</group>
</group>
</group>
</group>
</group>
</group>
</dimension>
</pivotTable>
</command>
</outputTree>
This is the c#
XElement root = XElement.Load(Page.Server.MapPath(#"oril.xml"));
XNamespace ad = "http://www.ibm.com/software/analytics/spss/xml/oms";
var cats = from cat in root.Descendants(ad + "dimension").Where
(cat => (string)cat.Attribute("axis") == "column" && (string)cat.Attribute("text") == "Variables")
select new
{
BATCH_NO = cat.Parent.Parent.Attribute("number").Value,
RUN_NO = cat.Parent.Attribute("number").Value,
//// 1
BATCH_RUN_ID = cat.Descendants(ad + "category").Elements(ad + "cell")
.Where(a => (string)a.Attribute("varName") == "BATCH_RUN_ID")
.Select(c => c.Attribute("number").Value),
//// 2
APP_ID = cat.Descendants(ad + "category").Elements(ad + "cell")
.Where(a => (string)a.Attribute("varName") == "APP_ID")
.Select(c => c.Attribute("label").Value),
//// 3
TEST_DATE = cat.Descendants(ad + "category").Elements(ad + "cell")
.Where(a => (string)a.Attribute("varName") == "TEST_DATE")
.Select(c => c.Attribute("date").Value),
////
//// Another 12
////
};
foreach (var cat in cats)
{
foreach (string s in cat.BATCH_RUN_ID)
{
xmlTitle.Text += "BATCH_NO: " + cat.BATCH_NO + " </br>";
xmlTitle.Text += "RUN_NO: " + cat.RUN_NO + " </br>";
xmlTitle.Text += "BATCH_RUN_ID: " + s + " </br>";
}
foreach (string s in cat.APP_ID)
{
xmlTitle.Text += "APP_ID: " + s + " </br>";
i_APP_ID = s;
}
foreach (string s in cat.TEST_DATE)
{
xmlTitle.Text += "TEST_DATE: " + s + " </br>";
i_TEST_DATE = s;
}
foreach (string s in cat.CB_USED)
{
xmlTitle.Text += "CB_USED: " + s + " </br>";
i_CB_USED = s;
}
////
//// Another 12
////
}

You could use Objects, since this is an Object Oriented Language, to ease some of your .Descendants().Elements() pain.
public class Category
{
public readonly XElement self;
public readonly XNamespace ns;
public Category(XNamespace xn, XElement cat) { self = cat; ns = xn; }
public string Name { get { return (string)self.Attribute("varName"); } }
public Cell Cell { get { return _Cell ?? (_Cell = new Cell(self.Elements(ns+"cell").First())); } }
Cell _Cell;
}
public class Cell
{
public readonly XElement self;
public Cell(XElement cell) { self = cell; }
public string Name { get { return (string)self.Attribute("varName"); } }
public string Number { get { return (string)self.Attribute("number"); } }
public string Date { get { return (string)self.Attribute("date"); } }
public string Label { get { return (string)self.Attribute("label"); } }
}
public class Dimension
{
public readonly XElement self;
public readonly XNamespace ns;
public Dimension(XNamespace xn, XElement dim) { self = dim; ns = xn; }
public string Axis { get { return (string)self.Attribute("axis"); } }
public string Text { get { return (string)self.Attribute("text"); } }
public string BatchNo { get { return self.Parent.Parent.Attribute("number").Value } }
public string RunNo { get { return self.Parent.Attribute("number").Value } }
public Category[] Categories
{ get { return _Categories ?? (_Categories = self.Elements(ns + "category")
.Select(cat => new Category(ns, cat))
.ToArray()); }
}
Category[] _Categories;
}
Then to use your root and ad defined in your post. If nothing else, it is more readable,
but it should be faster since once a Cell is created in a Category, it doesn't need to find it
on every cell call. And likewise with each category in a dimension.
var dims = root.Descendants(ad + "dimension")
.Select(dim => new Dimension(ad, dim))
.Where(Dim => Dim.Axis == "column" && Dim.Text == "Variables");
var cats = dims.Select(dim => new
{
BATCH_NO = dim.BatchNo,
RUN_NO = dim.RunNo,
//// 1
BATCH_RUN_ID = dim.Categories
.Where(cat => cat.Name == "BATCH_RUN_ID")
.Select(cat => cat.Cell.Number),
//// 2
APP_ID = dim.Categories
.Where(cat => cat.Name == "APP_ID")
.Select(cat => cat.Cell.Label),
//// etc
}
ps I typed this manually, it may not directly compile as is, but it would be something simple like a missing ;

First thing first,
when you need to concat alot of loop String like that you need to use StringBuilder to help it first
example:
StringBuilder sb = new StringBuilder();
foreach (var cat in cats)
{
foreach (string s in cat.BATCH_RUN_ID)
{
//xmlTitle.Text += "BATCH_NO: " + cat.BATCH_NO + " </br>";
sb.append("BATCH_NO: ");
sb.append( cat.BATCH_NO );
sb.append(" </br>");
// more and more, without using String + String
}
}
//at the end of the loop, just put it back to xml text
xmlTitle.Text = sb.toString();

Related

Iterate through an XML document [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 11 months ago.
Improve this question
What's the efficient way to iterate through this XML document using C#? This document is the result of relationship between two apps.
Hence there is the info for the user, and the info for the request itself.
Thank you in advance.
<?xml version="1.0" encoding="utf-16"?>
<Records count="2">
<Metadata>
<FieldDefinitions>
<FieldDefinition id="1001" name="Request ID" alias="Q_ID" />
<FieldDefinition id="1002" name="User" alias="Target" />
<FieldDefinition id="1003" name="Which item(s) is the access requested for?" alias="Which_items_is_requested" />
<FieldDefinition id="1004" name="Select specific(s) for item1" alias="item1" />
<FieldDefinition id="1005" name="Select specific(s) for item2" alias="item2" />
<FieldDefinition id="1006" name="Select specific(s) for item3" alias="item3" />
<FieldDefinition id="101" name="First Name" alias="First_Name" />
<FieldDefinition id="102" name="Last Name" alias="Last_Name" />
<FieldDefinition id="100" name="Email" alias="Email" />
</FieldDefinitions>
</Metadata>
<LevelCounts>
<LevelCount id="989" count="2" />
<LevelCount id="85" count="2" />
</LevelCounts>
<Record contentId="1092725" levelId="989" moduleId="564">
<Record contentId="736205" levelId="85" moduleId="84">
<Field id="100" type="1">john.smith#abc.com</Field>
<Field id="101" type="1">John</Field>
<Field id="102" type="1">Smith</Field>
</Record>
<Field id="1003" type="4">
<ListValues>
<ListValue id="11" displayName="Issues Management">item1</ListValue>
<ListValue id="13" displayName="Master Control Procedures">item3</ListValue>
</ListValues>
</Field>
<Field id="1001" type="6">123123</Field>
<Field id="1002" type="9">
<Reference id="736205">John Smith</Reference>
</Field>
<Field id="1005" type="9">
<Reference id="3">item11</Reference>
<Reference id="3">item12</Reference>
</Field>
<Field id="1006" type="9" />
<Field id="1004" type="9">
<Reference id="7">item31</Reference>
<Reference id="8">item32</Reference>
</Field>
</Record>
<Record contentId="1092759" levelId="989" moduleId="564">
<Record contentId="775678" levelId="85" moduleId="84">
<Field id="100" type="1">Peter.Smith#abc.com</Field>
<Field id="101" type="1">Peter</Field>
<Field id="102" type="1">Smith</Field>
</Record>
<Field id="1003" type="4">
<ListValues>
<ListValue id="11" displayName="Issues Management">item1</ListValue>
<ListValue id="12" displayName="Master Control Procedures">item2</ListValue>
<ListValue id="13" displayName="Control Procedure">item3</ListValue>
</ListValues>
</Field>
<Field id="1001" type="6">123124</Field>
<Field id="1002" type="9">
<Reference id="775678">Peter Smith</Reference>
</Field>
<Field id="1005" type="9">
<Reference id="3">item11</Reference>
<Reference id="4">item12</Reference>
</Field>
<Field id="1006" type="9">
<Reference id="5">item21</Reference>
<Reference id="6">item22</Reference>
</Field>
<Field id="1004" type="9">
<Reference id="7">item31</Reference>
<Reference id="8">item32</Reference>
</Field>
</Record>
</Records>
What's the efficient way to iterate through this XML document using C#? This document is the result of relationship between two apps.
Hence there is the info for the user, and the info for the request itself.
Thank you in advance.
From the comments, your question is how you deserialize the nested records.
A class structure like this seems to work:
public class Records
{
[XmlElement("Record")]
public List<Record> Items { get; } = new();
}
public class Record
{
[XmlAttribute("contentId")]
public int ContentId { get; set; }
[XmlElement("Record")]
public Record ChildRecord { get; set; }
[XmlElement("Field")]
public List<Field> Fields { get; } = new();
}
public class Field
{
[XmlAttribute("id")]
public int Id { get; set; }
[XmlElement("Reference")]
public List<Reference> References { get; } = new();
[XmlArray("ListValues")]
public List<ListValue> ListValues { get; } = new();
[XmlText]
public string Content { get; set; }
}
public class Reference
{
[XmlAttribute("id")]
public int Id { get; set; }
}
public class ListValue
{
[XmlAttribute("id")]
public int Id { get; set; }
[XmlText]
public string Content { get; set; }
}
See this doc on how to use the various attributes.
I haven't bothered to deserialize all of the attributes, or the Metadata or LevelCounts sections -- those are an exercise for the reader!
Use an XmlSerializer, for example:
using var reader = new StringReader(input);
var records = (Records)new XmlSerializer(typeof(Records)).Deserialize(reader);
See it on dotnetfiddle.net.
To flatten data into one table is messy but can be done. See below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Data;
namespace ConsoleApplication21
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
StreamReader sReader = new StreamReader(FILENAME);
//skip 1st line containing UTF-16
sReader.ReadLine();
XDocument doc = XDocument.Load(sReader);
DataTable dt = new DataTable();
dt.Columns.Add("Field Id", typeof(int));
dt.Columns.Add("Field Name", typeof(string));
dt.Columns.Add("Alias", typeof(string));
dt.Columns.Add("Main Content ID", typeof(int));
dt.Columns.Add("Main Level ID", typeof(int));
dt.Columns.Add("Main Module ID", typeof(int));
dt.Columns.Add("Sub Content ID", typeof(int));
dt.Columns.Add("Sub Level ID", typeof(int));
dt.Columns.Add("Sub Module ID", typeof(int));
dt.Columns.Add("Type", typeof(int));
dt.Columns.Add("Field Value Name", typeof(string));
dt.Columns.Add("Field Text", typeof(string));
dt.Columns.Add("List Value Id", typeof(int));
dt.Columns.Add("Display Name", typeof(string));
dt.Columns.Add("List Value Text", typeof(string));
dt.Columns.Add("Reference Id", typeof(int));
dt.Columns.Add("Reference Text", typeof(string));
foreach (XElement fieldDefinition in doc.Descendants("FieldDefinition"))
{
DataRow newRow = dt.Rows.Add();
newRow["Field Id"] = (int)fieldDefinition.Attribute("id");
newRow["Field Name"] = (string)fieldDefinition.Attribute("name");
newRow["Alias"] = (string)fieldDefinition.Attribute("alias");
}
foreach (XElement record in doc.Root.Elements("Record"))
{
int mainContentId = (int)record.Attribute("contentId");
int mainLevelId = (int)record.Attribute("levelId");
int mainModuleId = (int)record.Attribute("moduleId");
foreach(XElement subRecord in record.Elements("Record"))
{
int subContentId = (int)record.Attribute("contentId");
int subLevelId = (int)record.Attribute("levelId");
int subModuleId = (int)record.Attribute("moduleId");
foreach (XElement field in subRecord.Elements("Field"))
{
int fieldId = (int)field.Attribute("id");
int fieldType = (int)field.Attribute("type");
string fieldName = (string)field;
DataRow fieldRow = dt.AsEnumerable().Where(x => x.Field<int>("Field Id") == fieldId).First();
fieldRow["Main Content ID"] = mainContentId;
fieldRow["Main Level ID"] = mainLevelId;
fieldRow["Main Module ID"] = mainModuleId;
fieldRow["Sub Content ID"] = subContentId;
fieldRow["Sub Level ID"] = subLevelId;
fieldRow["Sub Module ID"] = subModuleId;
fieldRow["Type"] = fieldType;
fieldRow["Field Value Name"] = fieldName;
}
foreach (XElement field in record.Elements("Field"))
{
int fieldId = (int)field.Attribute("id");
int fieldType = (int)field.Attribute("type");
string fieldText = (string)field;
int count = 0;
DataRow fieldRow = dt.AsEnumerable().Where(x => x.Field<int>("Field Id") == fieldId).First();
List<XElement> listValues = field.Descendants("ListValue").ToList();
List<XElement> references = field.Elements("Reference").ToList();
if (listValues.Count > 0)
{
foreach (XElement listValue in listValues)
{
count++;
int listValueId = (int)listValue.Attribute("id");
string displayName = (string)listValue.Attribute("displayName");
string listValueText = (string)listValue;
if (count > 1)
{
string fieldName = fieldRow.Field<string>("Field Name");
string alias = fieldRow.Field<string>("Alias");
fieldRow = dt.Rows.Add();
fieldRow["Field Id"] = fieldId;
fieldRow["Field Name"] = fieldName;
fieldRow["Alias"] = alias;
}
fieldRow["Main Content ID"] = mainContentId;
fieldRow["Main Level ID"] = mainLevelId;
fieldRow["Main Module ID"] = mainModuleId;
fieldRow["Type"] = fieldType;
if(fieldText.Length > 0) fieldRow["Field Text"] = fieldText;
fieldRow["List Value Id"] = listValueId;
fieldRow["Display Name"] = displayName;
fieldRow["List Value Text"] = listValueText;
}
}
count = 0;
if (references.Count > 0)
{
foreach (XElement reference in references)
{
count++;
int referenceId = (int)reference.Attribute("id");
string referenceText = (string)reference;
if (count > 1)
{
string fieldName = fieldRow.Field<string>("Field Name");
string alias = fieldRow.Field<string>("Alias");
fieldRow = dt.Rows.Add();
fieldRow["Field Id"] = fieldId;
fieldRow["Field Name"] = fieldName;
fieldRow["Alias"] = alias;
}
fieldRow["Main Content ID"] = mainContentId;
fieldRow["Main Level ID"] = mainLevelId;
fieldRow["Main Module ID"] = mainModuleId;
fieldRow["Type"] = fieldType;
if (fieldText.Length > 0) fieldRow["Field Text"] = fieldText;
fieldRow["Reference Id"] = referenceId;
fieldRow["Reference Text"] = referenceText;
}
}
if((listValues.Count == 0) && (references.Count == 0))
{
fieldRow["Type"] = fieldType;
if (fieldText.Length > 0) fieldRow["Field Text"] = fieldText;
}
}
}
}
dt = dt.AsEnumerable().OrderBy(x => x.Field<int>("Field Id")).CopyToDataTable();
}
}
}

Add new XEelement and Update XML.Descendent

I have this code that works well, but I can't seem to figure out how to actually save the update XML.descendents list = JobNames to the XML document.
public void ProcessJobNames(String JobName = null, Boolean rRemove = false)
{
System.Xml.Linq.XDocument xml = System.Xml.Linq.XDocument.Load(_JobPath + #"\" + _ConfigFile);
var JobNames = xml.Descendants("setting").Where(xe => xe.Attribute("key").Value.Contains("JobType")).ToList();
System.Xml.Linq.XElement copyXE = null;
int JobCount = 0;
foreach (System.Xml.Linq.XElement strJobName in JobNames)
{
//strJobNames.Add(strJobName.LastAttribute.Value.ToString());
JobCount = JobCount + 1;
strJobName.FirstAttribute.Value = "JobType" + (JobCount).ToString();
copyXE = strJobName; // Make a copy of the Job
// Check if the JobName is in the Job Processor to be Removed
if ((JobName != null) && (rRemove == true) && (strJobName.LastAttribute.Value.ToString().Contains(JobName)))
{
// Remove the Job from the Configuration File
strJobName.Remove();
JobCount = JobCount - 1;
}
// Check if the JobName is in the Job Processor to be Added
if ((JobName != null) && (rRemove == false) && (strJobName.LastAttribute.Value.ToString().Contains(JobName)))
{
// Add the Job from the Configuration File
strJobName.Remove();
JobCount = JobCount - 1;
}
}
// Add the new Job
if ((JobName != null) && (rRemove == false)) {
File.Copy(_RulesFolder + cmboRules.Text, _JobPath + cmboRules.Text);
System.Xml.Linq.XElement newJob = new System.Xml.Linq.XElement(copyXE);
newJob.FirstAttribute.Value = "JobType" + (JobCount + 1).ToString();
newJob.LastAttribute.Value = _JobPrefix + cmboRules.Text;
copyXE.Parent.Add(newJob)
}
// Save the XML Configuration File
xml.Save(_JobPath + _ConfigFile);
}
I think I'm missing something after "JobNames.Add(newJob)" to update the descendants in the XML document.
To add a node in runtime, You could use XElement.Parent.Add()method to add child. For example,
foreach(var job in jobList)
{
if(job.Attribute("name").Value == "job1") //Any condition on which new node is to be added
{
var copy = job;
copy.Attribute("name").Value = "job3";
job.Parent.Add(copy);
}
}
Sample
Input
<root>
<jobs>
<job name="job1"></job>
<job name="job2"></job>
</jobs>
</root>
Output
<root>
<jobs>
<job name="job3"></job>
<job name="job2"></job>
<job name="job3"></job>
</jobs>
</root>

Convert a XML to CSV using C#

How to convert a XML file to a CSV file in C#, showing only these Tags: <original-impot-no>, <price>, <Small-price>, <Big-price>?
sample XML code: it represents one line among several lines, and every line may contains several <product-lineitem>Tags
<?xml version="1.0" encoding="UTF-8"?>
<impots xmlns="http://www.google.com/xml/impot//20016-02-31">
<impot impot-no="W0110891258">
<impot-date>2017-12-10T22:33:35.000Z</impot-date>
<prop-by>Yallo</prop-by>
<original-impot-no>891258</original-impot-no>
<currency>EUR</currency>
<server-locale>Esp</server-locale>
<lax>gross</lax>
<current-impot-no>123358</current-impot-no>
<product-lineitems>
<product-lineitem>
<price>450</price>
<red>6.50</red>
<Small-price>39</Small-price>
<Big-price>3229</Big-price>
<lineitem-text>Grand create</lineitem-text>
<basis>234.00</basis>
</product-lineitem>
</product-lineitems>
<product-lineitem>
<price>432</price>
<red>12</red>
<Small-price>44</Small-price>
<Big-price>34</Big-price>
<lineitem-text>Small create</lineitem-text>
<basis>44.00</basis>
</product-lineitem>
</product-lineitems>
</impot>
</impots>
I should get someting like this in y CSV file:
891258;450;39;229
891258;432;44;34
the C# code:
the problem that Im facing with this code is that I can not retrieve the descandent of the TAG <impot>
XmlTextReader xtr = new XmlTextReader(#"C:\Temp_Convert\Impot.xml");
StringBuilder dataToBeWritten = new StringBuilder();
while (xtr.Read())
{
if (xtr.NodeType == XmlNodeType.Element && xtr.Name == "original-impot-no")
{
string s1 = xtr.ReadElementString();
dataToBeWritten.Append(s1);
dataToBeWritten.Append(";");
}
else if (xtr.NodeType == XmlNodeType.Element && xtr.Name == "price")
{
string s2 = xtr.ReadElementString();
dataToBeWritten.Append(s2);
dataToBeWritten.Append(";");
}
else if (xtr.NodeType == XmlNodeType.Element && xtr.Name == "Small-price")
{
string s2 = xtr.ReadElementString();
dataToBeWritten.Append(s2);
dataToBeWritten.Append(";");
dataToBeWritten.Append(0);
dataToBeWritten.Append(Environment.NewLine);
}
else if (xtr.NodeType == XmlNodeType.Element && xtr.Name == "Big-price")
{
string s2 = xtr.ReadElementString();
dataToBeWritten.Append(s2);
dataToBeWritten.Append(";");
dataToBeWritten.Append(0);
dataToBeWritten.Append(Environment.NewLine);
}
}
File.WriteAllText(#"C:\Temp_Convert\Impot.csv", dataToBeWritten.ToString());
}
Can somebody please propose a solution, thank you so much in advance.
You have an invalid XML. I guess here is the correct format.
<?xml version="1.0" encoding="UTF-8"?>
<impots xmlns="http://www.google.com/xml/impot//20016-02-31">
<impot impot-no="W0110891258">
<impot-date>2017-12-10T22:33:35.000Z</impot-date>
<prop-by>Yallo</prop-by>
<original-impot-no>891258</original-impot-no>
<currency>EUR</currency>
<server-locale>Esp</server-locale>
<lax>gross</lax>
<current-impot-no>123358</current-impot-no>
<product-lineitems>
<product-lineitem>
<price>450</price>
<red>6.50</red>
<Small-price>39.00</Small-price>
<Big-price>3229.00</Big-price>
<lineitem-text>Grand create</lineitem-text>
<basis>234.00</basis>
</product-lineitem>
</product-lineitems>
<product-lineitems>
<product-lineitem>
<price>432</price>
<red>12</red>
<Small-price>44.00</Small-price>
<Big-price>34.00</Big-price>
<lineitem-text>Small create</lineitem-text>
<basis>44.00</basis>
</product-lineitem>
</product-lineitems>
</impot>
</impots>
You couldn't retrieve the descendant because you are not including the namespace http://www.google.com/xml/impot//20016-02-31.
Here is how the code should be.
XNamespace ns = "http://www.google.com/xml/impot//20016-02-31";
var results = xDocument.Descendants(ns + "impot");
Then, you need to modify your query to retrieve elements that you need.
Here is the sample. I assume that product-lineitems only has one child product-lineitem.
var results = xDocument.Descendants(ns + "impot").Select(x => new {
ImpotNo = x.Attribute("impot-no")?.Value,
ProductLineItems = x.Descendants(ns + "product-lineitems").Select(y => new
{
Item = y.Descendants(ns + "product-lineitem").Select(z => new
{
Price = z.Element(ns + "price")?.Value,
SmallPrice = z.Element(ns + "Small-price")?.Value,
BigPrice = z.Element(ns + "Big-price")?.Value,
}).FirstOrDefault()
})
});
foreach (var result in results)
{
foreach (var productLine in result.ProductLineItems)
{
dataToBeWritten.Append(result.ImpotNo);
dataToBeWritten.Append(";");
dataToBeWritten.Append(productLine.Item.Price);
dataToBeWritten.Append(";");
dataToBeWritten.Append(productLine.Item.SmallPrice);
dataToBeWritten.Append(";");
dataToBeWritten.Append(productLine.Item.BigPrice);
dataToBeWritten.Append(";");
dataToBeWritten.Append(0);
dataToBeWritten.Append(Environment.NewLine);
}
}
Well, first I tried to reformat your XML to be a bit more readable but the tag structure still seems wrong...
<impots
xmlns="http://www.google.com/xml/impot//20016-02-31">
<impot impot-no="W0110891258">
<impot-date>2017-12-10T22:33:35.000Z</impot-date>
<prop-by>Yallo</prop-by>
<original-impot-no>891258</original-impot-no>
<currency>EUR</currency>
<server-locale>Esp</server-locale>
<lax>gross</lax>
<current-impot-no>123358</current-impot-no>
<product-lineitems>
<product-lineitem>
<price>450</price>
<red>6.50</red>
<Small-price>39.00</Small-price>
<Big-price>3229.00</Big-price>
<lineitem-text>Grand create</lineitem-text>
<basis>234.00</basis>
-
</product-lineitems>
-
</product-lineitem>
<product-lineitems>
<product-lineitem>
<price>432</price>
<red>12</red>
<Small-price>44.00</Small-price>
<Big-price>34.00</Big-price>
<lineitem-text>Small create</lineitem-text>
<basis>44.00</basis>
</product-lineitems>
</product-lineitem>
Nonetheless, I'm guessing this line is incorrect since "impot-no" is an attribute...
impot-no = (string)x.Element("impot impot-no")
Perhaps you meant that line to be...
impot-no = (string)x.Attribute("impot-no").Value
Going by memory -- hopefully that is the correct way to retrieve an attribute.
Review the following code. Note the use of SelectMany to get the impot items to construct the desired object models.
XNamespace ns = "http://www.google.com/xml/impot//20016-02-31";
var results = xDocument.Descendants(ns + "impot")
.SelectMany(impot => impot.Descendants(impot.Name.Namespace + "product-lineitem")
.Select(item => new {
ImpotNo = (string)impot.Element(impot.Name.Namespace + "original-impot-no"),
Price = (string)item.Element(item.Name.Namespace + "price"),
SmallPrice = (string)item.Element(item.Name.Namespace + "Small-price"),
BigPrice = (string)item.Element(item.Name.Namespace + "Big-price"),
})
).ToList();
for (int i = 0; i < results.Count; i++) {
dataToBeWritten.Append(results[i].ImpotNo);
dataToBeWritten.Append(";");
dataToBeWritten.Append(results[i].Price);
dataToBeWritten.Append(";");
dataToBeWritten.Append(results[i].SmallPrice);
dataToBeWritten.Append(";");
dataToBeWritten.Append(results[i].BigPrice);
dataToBeWritten.Append(";");
dataToBeWritten.Append(0);
dataToBeWritten.Append(Environment.NewLine);
}
Also note the syntax used for the properties.

How can i specify the code to read through all <Module> that are in only the <Keystage3> node

Here is the current code I have which outputs all tags with the name Module however I want to specify it to read through only the <keystage3> and so read all modules which are within this tag.
XElement root = XElement.Load("Data.xml");
var subjects = from subject in root.Descendants()
where subject.Name.LocalName.Contains("Subject")
select new
{
subname = subject.Element("subjectName").Value,
subid = subject.Element("subjectId").Value,
subvalue = subject.Element("subjectvalue").Value
};
foreach (var subject in subjects)
{
Console.WriteLine(subject);
//you can use subject like this:
string subName = subject.subname;
string subID = subject.subid;
string subValue = subject.subvalue;
TextBox[] info = new TextBox[6];
textBox1.AppendText("Subject Name : " + subName + " Subject Id : " + subID + " Subject Value : " + subValue + " " );
}
my xml file contains :
<PersonDetails>
<PersonTitle>Teacher</PersonTitle>
<Keystage3>
<Subject>
<subjectName>maths</subjectName>
<subjectId>qq1</subjectId>
<subjectvalue>20</subjectvalue>
<subjectscore />
</Subject>
<Subject>
<subjectName>science</subjectName>
<subjectId>sla1s</subjectId>
<subjectvalue>25</subjectvalue>
<subjectscore />
</Subject>
</Keystage3>
<Keystage4>
<Subject>
<subjectName>ICT</subjectName>
<subjectId>a1</subjectId>
<subjectvalue>10</subjectvalue>
<subjectscore />
</Subject>
</Keystage4>
</PersonDetails>
So my question is How can i specify the code to read through all <Module> that are in only the <Keystage3> as the current code gives me all the <Module> for the entire xml file.
You pre-filter the descendants by "keystage3" and then filter down like you already did:
internal class Program
{
private static string txt = #"
<PersonDetails><PersonTitle>Teacher</PersonTitle><Keystage3><Subject>
<subjectName>maths</subjectName><subjectId>qq1</subjectId><subjectvalue>20</subjectvalue>
<subjectscore /></Subject><Subject><subjectName>science</subjectName>
<subjectId>sla1s</subjectId><subjectvalue>25</subjectvalue><subjectscore />
</Subject></Keystage3><Keystage4><Subject><subjectName>ICT</subjectName>
<subjectId>a1</subjectId><subjectvalue>10</subjectvalue><subjectscore />
</Subject></Keystage4></PersonDetails>";
static void Main()
{
XElement root = XElement.Parse(txt); // Load -> Parse
var subjects = from stage in root.Descendants()
where stage.Name.LocalName == "Keystage3"
from subject in stage.Descendants()
where subject.Name.LocalName.Contains("Subject")
select new
{
subname = subject.Element("subjectName").Value,
subid = subject.Element("subjectId").Value,
subvalue = subject.Element("subjectvalue").Value
};
foreach (var subject in subjects)
Console.WriteLine(subject);
Console.ReadLine();
}
}
Output:
{ subname = maths, subid = qq1, subvalue = 20 }
{ subname = science, subid = sla1s, subvalue = 25 }

Linq to XML adding to List Problem send to repeater

I need to get data from a XML file from Node1>Node2
Filter data to group from the xml by X Node where X node is equals A,Y,Z..
Group all elements with same X Node value ordered by date
Store first element of the group(as ordered by date would be by the latest), and the rest in a subgroup of the same object.
If in the filter fails to find one Node equals(A,X or Z) then it creates an first element with hardcoded data for the fist element.
Once this process is finish then send the object (list<>) to a repeater.
I have nearly everything but I don’t know how to do it for when the filter fails to find a Node that is equals to (A,X or Z)
Code Example.
private void BindOutputRepeaterLongTermCondition()
{
XDocument xd = XDocument.Parse(element.OuterXml);
if (xd != null)
{
var results = (from el1 in xd.Descendants("Node1")
from el2 in el1.Descendants("Node2")
where el2.Element("Date") != null
&&(el2.Element("Code").Value.StartsWith("A")
|| el2.Element("Code").Value.StartsWith("Y")
|| el2.Element("Code").Value.StartsWith("Z")
|| el2.Element("Code").Value.StartsWith("B")
)
orderby el2.Element("Date").Value descending
group el2 by el2.Element("Code").Value
into CodeGroup
select new Table(CodeGroup.First())
{
SubTable = (from subCodes in CodeGroup
select new Table(subCodes)).Skip(1).ToList()
}).ToList<Table>();
this.repeaterTable.DataSource = results;
this.repeaterTable.DataBind();
}
}
protected void repeater_Table_ItemDataBound(object sender, System.Web.UI.WebControls.RepeaterItemEventArgs e)
{
RepeaterItem item = e.Item;
if ((item.ItemType == ListItemType.Item) || (item.ItemType == ListItemType.AlternatingItem))
{
var recentTable = (Table)item.DataItem;
var TableRepeater = (Repeater)item.FindControl("rptSubTable");
TableRepeater.DataSource = recentTable.SubTable;
TableRepeater.DataBind();
}
}
}
public class Table
{
public string Date { get; set; }
public string Rubric { get; set; }
public string Value { get; set; }
public List<Table> SubTable { get; set; }
public Table(XElement xml)
{
Date = xml.Element("Date") != null ? xml.Element("Date").Value : "";
Code = xml.Element("Code") != null ? xml.Element("Code").Value : "";
var a = (string)xml.Element("Code").Value;
if (a.StartsWith("A"))
{
Name = "Row 1";
}
else if (a.StartsWith("Y"))
{
Name = "Row 2";
}
else if (a.StartsWith("Z"))
{
Name = "Row 3";
}
else if (a.StartsWith("B"))
{
Name = "Row 4";
}
//Tried the following but it does not work.
else if (!a.StartsWith("A") && !a.StartsWith("Y") && !a.StartsWith("B")){
Name = "Row 3";
Value = "Not Found";
}
}
}
XML Code example
<Node1>
<Node2>
<Date>2009-07-16</Date>
<Code>A</Code>
<Value>Some Value</Value>
</Node2>
<Node2>
<Date>2008-02-09</Date>
<Code>Y</Code>
<Value>Some Value</Value>
</Node2>
<Node2>
<Date>2008-02-09</Date>
<Code>Z</Code>
<Value>Some Value</Value>
</Node2>
<Node2>
<Date>2008-07-16</Date>
<Code>A</Code>
<Value>Some Value</Value>
</Node2>
<Node2>
<Date>2006-02-09</Date>
<Code>Y</Code>
<Value>Some Value</Value>
</Node2>
<Node2>
<Date>2001-02-09</Date>
<Code>Z</Code>
<Value>Some Value</Value>
</Node2>
</Node1>
Any help would be appreciated.
Update
The problem is that I need to display all elements whether they are on the xml or not.
At the moment I can only display existing elements.
If one of the elements (by "Code") is not found I need to display hardcoded name of the element and value "No Value".
Instead of the final elseif:
else if (!a.StartsWith("A") && !a.StartsWith("Y") && !a.StartsWith("B")){
Name = "Row 3";
Value = "Not Found";
}
Have you tried
else
{
Name = "Row n";
Value = "Not Found";
}
It should just fall through to that branch if all other conditions are not met (unless I am missing something)

Categories

Resources