XDocument to List of object - c#

The content of an XDocument is the XML below.
I'd like to get a List(), see at the end of this message.
<myXml>
<myDatas code="01">
<myVar name="myvar" value="A" />
<myData name="A" value="A1" />
<myData name="B" value="B1" />
</myDatas>
<myDatas code="02">
<myVar name="myvar" value="B" />
<myData name="A" value="A2" />
<myData name="D" value="D2" />
</myDatas>
</myXml>
public class MyData
{
public string MainCode { get; set; }
public string Code { get; set; }
public string Value { get; set; }
}
I'd like a List() this content should be like this :
new MyData { MainCode = "01"; Code = "A"; Value = "A1" };
new MyData { MainCode = "01"; Code = "B"; Value = "B1" };
new MyData { MainCode = "02"; Code = "A"; Value = "A2" };
new MyData { MainCode = "02"; Code = "D"; Value = "D2" };

Sure - so you need something like this:
var query = from datas in doc.Root.Elements("myDatas")
let code = (string) datas.Attribute("code")
from data in datas.Elements("myData")
select new MyData {
MainCode = code,
Code = (string) data.Attribute("name"),
Value = (string) data.Attribute("value"),
};
var list = query.ToList();
Note the multiple from clauses to flatten the results.
Another alternative would have been to just find all the "leaf" elements and fetch the code part from the parent:
var query = from data in doc.Descendants("myData")
select new MyData {
MainCode = (string) data.Parent.Attribute("code"),
Code = (string) data.Attribute("name"),
Value = (string) data.Attribute("value"),
};
var list = query.ToList();
EDIT: If your document uses namespaces, that's easy too:
XNamespace ns = "http://the-uri-of-the-namespace";
var query = from data in doc.Descendants(ns + "myData")
...
This uses the XName operator +(XNamespace, string) overloaded operator.

Related

How to read multiple attributes from an xml file

I have the following xml:
<?xml version="1.0" encoding="UTF-8"?>
<TestRun id="2fc10ef6-b97f-49e5-a58d-863dfb599cb3" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<Times creation="2019-08-26T11:27:34.3642040+00:00" queuing="2019-08-26T11:27:34.3642190+00:00" start="2019-08-26T11:27:29.1640690+00:00" finish="2019-08-26T11:29:28.0320260+00:00" />
<TestSettings name="default" id="3c3c8ad0-9076-4c83-a283-03f5490f906b">
<Deployment runDeploymentRoot="_9e3d0007c2b9 2019-08-26 11:27:34" />
</TestSettings>
<Results>
<UnitTestResult testName="FirstName" outcome="Passed" testListId="1">
<Output>
</Output>
</UnitTestResult>
<UnitTestResult testName="SecondName" outcome="Passed" testListId="2">
<Output>
</Output>
</UnitTestResult>
<UnitTestResult testName="Thirdname" outcome="Passed" testListId="3">
<Output>
</Output>
</UnitTestResult>
</Results>
</TestRun>
And i have the following classes:
{
public string testName { get; set; }
public string outcome { get; set; }
}
public DtoHeader ReadXmlFile()
{
var binDirectory = Path.GetDirectoryName(GetType().GetTypeInfo().Assembly.Location);
var file = Path.Combine(binDirectory, "myfile.xml");
var xDocument = XDocument.Load(file);
XmlNamespaceManager nameSpaceManager = new XmlNamespaceManager(new NameTable());
nameSpaceManager.AddNamespace("ns", "http://microsoft.com/schemas/VisualStudio/TeamTest/2010");
var items = xDocument.Root.XPathSelectElements("./ns:Results", nameSpaceManager).ToArray();
if (!items.Any())
{
}
return new DtoHeader
{
testName = items.Descendants().Attributes("testName").First().Value,
};
}
I will like to extract the value of the attributes testName and outcome and put these values in a list.However, I have not been able to do this after going through multiple examples.
items.Descendants().Attributes("testName") returns IEnumerable of XAttribute.
You need to select the Value member of each XAttibute and return a list.
var list = items.Descendants().Attributes("testName").Select(t => t.Value).ToList();
Edit:
Return as DtoHeader List:
var list = items.Descendants().Attributes("testName").Select(t =>
new DtoHeader
{
testName = t.Value
}
).ToList();
To get a list of DtoHeaders using Linq2Xml you could use the following approach:
var headers = xDocument.Root.XPathSelectElements("./ns:Results", nameSpaceManager)
.Elements()
.Select(x => new DtoHeader
{
testName = x.Attribute("testName").Value,
outcome = x.Attribute("outcome").Value
})
.ToList();
I suggest to use xmltocsharp to convert the xml to classes and use XmlSerializerto do the rest of the job. Here is the simplified code to get the desired result.
string requestBody = ReadFile("XMLFile1.xml");
XmlSerializer serializer = new XmlSerializer(typeof(TestRun));
using (TextReader reader = new StringReader(requestBody))
{
//convert the xml to object
TestRun testRun = (TestRun)serializer.Deserialize(reader);
foreach (var result in testRun.Results.UnitTestResult)
{
Console.WriteLine($"{result.TestName} : {result.Outcome}");
}
}
var doc = XDocument.Load(file);
var list = new System.Collections.Generic.List<DtoHeader>();
foreach (var n in doc.Descendants().First(node => node.Name.LocalName == "Results").Elements().Where(e => e.Name.LocalName == "UnitTestResult"))
{
list.Add(new DtoHeader
{
outcome = n.Attribute("outcome").Value,
testName = n.Attribute("testName").Value,
});
}

How do I combine these two different LINQ queries into one?

How do I write this so that I can get the Id and the Fist and Last name all in one query? Everything I have found via Search is database related and involves a Join or a Group, which I don't think is applicable to what I am trying to do. I want to print the order Id and the associated first and last name to the screen.
static void Main()
{
XDocument document = XDocument.Parse(GetXml());
var query = from el in document.Root.Elements("Order")
select new Orders
{
Id = (int)el.Element("Id")
};
foreach (var cc in query)
{
Console.WriteLine(cc.Id);
}
var info = from el in document.Root.Elements("Order").Elements("BillingAddress")
select new BillingAddress
{
FirstName = (string)el.Element("FirstName"),
LastName = (string)el.Element("LastName")
};
foreach (var cc in info)
{
Console.WriteLine("{0} {1}", cc.FirstName, cc.LastName);
}
Console.ReadLine();
}
private static String GetXml()
{
return
#"<ArrayOfOrder>
<Order>
<Id>1</Id>
<OrderGuid />
<BillingAddress>
<FirstName>Harvey</FirstName>
<LastName>Danger</LastName>
</BillingAddress>
</Order>
<Order>
<Id>2</Id>
<OrderGuid />
<BillingAddress>
<FirstName>Brian</FirstName>
<LastName>Brakes</LastName>
</BillingAddress>
</Order>
</ArrayOfOrder>";
}
Add a BillingAddress property to your Orders class then you can do the following:
var query = from el in document.Root.Elements("Order")
select new Orders
{
Id = (int) el.Element("Id"),
BillingAddress = new BillingAddress
{
FirstName = (string) el.Element("BillingAddress").Element("FirstName"),
LastName = (string) el.Element("BillingAddress").Element("LastName")
}
};
var query = from el in document.Root.Elements("Order")
select new Orders
{
Id = (int)el.Element("Id"),
Names = el.Elements("BillingAddress")
.Select(ba=>
new { FirstName = (string)ba.Element("FirstName"),
LastName = (string)ba.Element("LastName")
}
};

To return the multiple values from for loop

I have parsed the xml document and used a for loop to loop for getting different values in string, but when I try to return the value I get only the last value obtained, I want to return all the individual values so that I can store that values in any file format,
Below is my code,
XmlDocument xmlDOC = new XmlDocument();
xmlDOC.LoadXml(periodID_Value_Before_OffSet); // string storing my XML
var value = xmlDOC.GetElementsByTagName("value");
var xmlActions = new string[value.Count];
string values = "";
string Period1 = "";
string periodlevel_period1 = "";
var listOfStrings = new List<string>();
string modified_listofstrings = listOfStrings.ToString();
string arrayOfStrings = "";
for (int i = 0; i < value.Count; i++)
{
var xmlAttributeCollection = value[i].Attributes;
if (xmlAttributeCollection != null)
{
var action = xmlAttributeCollection["periodid"];
xmlActions[i] = action.Value;
values += action.Value + ",";
string vals = values.Split(',')[1];
string counts = values;
string[] periods = counts.Split(',');
Period1 = periods[i];
// periodlevel_period1 = Client.GetAttributeAsString(sessionId, Period1, "name", "");
modified_listofstrings = Client.GetAttributeAsString(sessionId, Period1, "name", "");
modified_listofstrings.ToArray().ToString();
//listOfStrings = periodlevel_period1;
}
}
return modified_listofstrings;
This modified_listofstrings string only return last on value, I want to return the array of the values all obtained while looping.
----------Updated question----------
below is my Sample XMl
<string xmlns="http://tempuri.org/">
<ResultSetHierarchy totalResultsReturned="1" totalResults="1" firstIndex="0" maxCount="-1">
<object id="SC.1938773693.238">
<measure.values>
<series id="SC.1938773693.108280985">
<value periodid="SC.1938773693.394400760" value="17" />
<value periodid="SC.1938773693.1282504058" value="15" />
<value periodid="SC.1938773693.1631528570" value="13" />
</series>
</object>
</ResultSetHierarchy>
</string>
I want output as "SC.1938773693.394400760":"17" and so on for all periodid
Based on the provided information I have updated the answer.
List<string> items = new List<string>();
XmlDocument xmlDOC = new XmlDocument();
xmlDOC.Load(#"E:\Delete Me\ConsoleApplication1\ConsoleApplication1\bin\Debug\List.xml");
var elements = xmlDOC.GetElementsByTagName("value");
foreach (var item in elements)
{
XmlElement value = (XmlElement)item;
items.Add(string.Format("{0}:{1}", value.GetAttribute("periodid"), value.GetAttribute("value")));
}
It looks like you're trying to:
Load an XmlDocument
Get a list of all the attributes of name 'periodid'
Look each periodid up using a webservice call
Return a list of the lookup results
If that is correct, the following method should do what you need:
public List<string> GetListOfData()
{
XmlDocument xmlDOC = new XmlDocument();
xmlDOC.LoadXml("<Html><value periodid='Yabba'>YabbaValue</value><value periodid='Dabba'>DabbaValue</value><value periodid='Doo'>DooValue</value></Html>"); // string storing my XML
var value = xmlDOC.GetElementsByTagName("value");
var listOfStrings = new List<string>();
for (int i = 0; i < value.Count; i++)
{
var xmlAttributeCollection = value[i].Attributes;
if (xmlAttributeCollection != null)
{
var action = xmlAttributeCollection["periodid"];
string Period1 = action.Value;
listOfStrings.Add(QPR_webService_Client.GetAttributeAsString(sessionId, Period1, "name", "") + ":" + value[i].InnerText);
}
}
return listOfStrings;
}

Parse Hierarchial Linq to XML

I am trying to parse this data:
<Product>
<ProductName>Climate Guard</ProductName>
<Tag>ClimateGuard</Tag>
<SupportPage>~/Support/ClimateGuard.aspx</SupportPage>
<ProductPage>~/Products/ClimateGuard.aspx</ProductPage>
<ProductCategories>
<ProductCategory>Climate Guard</ProductCategory>
<PartNumbers>
<PartNumber Primary="true">CLIMATE GUARD</PartNumber>
<PartNumber>CLIMATEGUARD LT</PartNumber>
<PartNumber>CLIMATE GUARD STARTER KIT</PartNumber>
<PartNumber>SENSOR MODULE</PartNumber>
<PartNumber>SWCH INP MODULE</PartNumber>
<PartNumber>TEMP SENSOR</PartNumber>
<PartNumber>HUMIDITY SENSOR</PartNumber>
<PartNumber>DOOR CONTACT</PartNumber>
<PartNumber>MOTION SENSOR</PartNumber>
<PartNumber>FLOOD DETECTOR</PartNumber>
<PartNumber>SMOKE DETECTOR</PartNumber>
<PartNumber>TILT SENSOR</PartNumber>
<PartNumber>SENSOR CABLE</PartNumber>
<PartNumber>PWR INP CABLE</PartNumber>
<PartNumber>100FT 2-WIRE</PartNumber>
<PartNumber>RJ25 COUPLER</PartNumber>
</PartNumbers>
</ProductCategories>
<Downloads>
<Download>
<Version>1.0.27</Version>
<Url>~/Files/Downloads/ClimateGuard_Firmware_1_0_27.bin</Url>
<Comment>Firmware</Comment>
</Download>
<Download>
<Version>1.0.6</Version>
<Url>~/Files/Downloads/ClimateGuard_BuiltInModule_1_0_6.bin</Url>
<Comment>Built-in Module</Comment>
</Download>
<Download>
<Version>1.0.2</Version>
<Url>~/Files/Downloads/ClimateGuard_SensorModule_1_0_2.bin</Url>
<Comment>Sensor Module</Comment>
</Download>
<Download>
<Version>1.0.0</Version>
<Url>~/Files/Downloads/ClimateGuard_SwitchInputModule_1_0_0.bin</Url>
<Comment>Switch Input Module</Comment>
</Download>
</Downloads>
</Product>
I am trying to get a List of part numbers, however, only the first appears:
Product Category Climate Guard
Part Number Climate Guard
What is wrong with my part numbers code:
public List<Products> GetProducts()
{
XElement myElement = XElement.Load(HttpContext.Current.Server.MapPath("~/App_Data/products.xml"));
var query = from a in myElement.Elements("Product")
select new Products
{
ProductName = a.Element("ProductName").Value,
Tag = a.Element("Tag").Value,
SupportPage = a.Element("SupportPage").Value,
ProductPage = a.Element("ProductPage").Value,
ProductCategories = from b in a.Elements("ProductCategories")
select new ProductCategories
{
ProductCategory = b.Element("ProductCategory").Value,
//PartNumbers = GetPartNumbers(myElement.Elements("Product").Elements("ProductCategories").Elements("PartNumbers").Elements("PartNumber"))
PartNumbers = from c in b.Elements("PartNumbers")
select new PartNumbers
{
PartNumber = c.Element("PartNumber").Value
}
},
Downloads = from bb in a.Elements("Downloads").Elements("Download")
select new Downloads
{
Comment = bb.Element("Comment").Value,
Url = bb.Element("Url").Value,
Version = bb.Element("Version").Value
},
};
return query.ToList();
}
All of the types (ProductName, Tag, etc.) are strings. PartNumbers is an IEnumerable.
Currently instead of getting collection of PartNumber element values, you are getting only element for their parent PartNumbers with value of first PartNumber child inside. If you want to have PartNumbers class instead of simple list of string values, then it should look like:
public class PartNumbers
{
// list instead of single value
public List<string> Numbers { get; set; }
}
And it should be parsed this way:
PartNumbers = new PartNumbers {
Numbers = b.Element("PartNumbers").Elements()
.Select(c => (string)c).ToList()
}
BTW why are you choosing so strange range variable names (b for ProductCategories elements, a for products, etc)? Also you can use simple List<string> to store part numbers (without creating class for that):
PartNumbers = b.Element("PartNumbers").Elements().Select(c => (string)c).ToList()
You may have forgotten the ToList() for ProductCategories, PartNumbers and Downloads.
public List<Products> GetProducts()
{
XElement myElement = XElement.Load(HttpContext.Current.Server.MapPath("~/App_Data/products.xml"));
var query = from a in myElement.Elements("Product")
select new Products
{
ProductName = a.Element("ProductName").Value,
Tag = a.Element("Tag").Value,
SupportPage = a.Element("SupportPage").Value,
ProductPage = a.Element("ProductPage").Value,
ProductCategories = (from b in a.Elements("ProductCategories")
select new ProductCategories
{
ProductCategory = b.Element("ProductCategory").Value,
//PartNumbers = GetPartNumbers(myElement.Elements("Product").Elements("ProductCategories").Elements("PartNumbers").Elements("PartNumber"))
PartNumbers = (from c in b.Elements("PartNumbers")
select new PartNumbers
{
PartNumber = c.Element("PartNumber").Value
}).ToList()
}).ToList(),
Downloads = (from bb in a.Elements("Downloads").Elements("Download")
select new Downloads
{
Comment = bb.Element("Comment").Value,
Url = bb.Element("Url").Value,
Version = bb.Element("Version").Value
}).ToList(),
};
return query.ToList();
}

Help with Linq to XML

Iv got two DB tables. One containing types(Id, Name) and the other holds datapoints (RefId, Date, Value) that are referenced by the types. I need to create a XML file with the following strukture:
<?xml version='1.0' encoding='utf-8' ?>
<root>
<type>
<name></name>
<data>
<date></date>
<temp></temp>
</data>
<data>
<date></date>
<temp></temp>
</data>
<data>
<date></date>
<temp></temp>
</data>
</type>
</root>
And iv got the following code to do this
public XmlDocument HelloWorld()
{
string tmp = "";
try
{
sqlConn.ConnectionString = ConfigurationManager.ConnectionStrings["NorlanderDBConnection"].ConnectionString;
DataContext db = new DataContext(sqlConn.ConnectionString);
Table<DataType> dataTypes = db.GetTable<DataType>();
Table<DataPoints> dataPoints = db.GetTable<DataPoints>();
var dataT =
from t in dataTypes
select t;
var dataP =
from t in dataTypes
join p in dataPoints on t.Id equals p.RefId
select new
{
Id = t.Id,
Name = t.Name,
Date = p.PointDate,
Value = p.PointValue
};
string xmlString = "<?xml version=\"1.0\" encoding=\"utf-8\" ?><root></root>";
XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(xmlString);
int count = 0;
foreach (var dt in dataT)
{
XmlElement type = xmldoc.CreateElement("type");
XmlElement name = xmldoc.CreateElement("name");
XmlNode nameText = xmldoc.CreateTextNode(dt.Name);
name.AppendChild(nameText);
type.AppendChild(name);
foreach(var dp in dataP.Where(dt.Id = dp.RefId))
{
XmlElement data = xmldoc.CreateElement("data");
XmlElement date = xmldoc.CreateElement("date");
XmlElement temp = xmldoc.CreateElement("temp");
XmlNode dateValue = xmldoc.CreateTextNode(dp.Date.ToString());
date.AppendChild(dateValue);
XmlNode tempValue = xmldoc.CreateTextNode(dp.Value.ToString());
temp.AppendChild(tempValue);
data.AppendChild(date);
data.AppendChild(temp);
type.AppendChild(data);
}
xmldoc.DocumentElement.AppendChild(type);
}
return xmldoc;
}
catch(Exception e)
{
tmp = e.ToString();
}
return null;
}
[Table(Name="DataTypes")]
public class DataType
{
[Column(IsPrimaryKey = true)]
public long Id;
[Column]
public string Name;
}
[Table(Name="DataPoints")]
public class DataPoints
{
[Column]
public long RefId;
[Column]
public DateTime PointDate;
[Column]
public double PointValue;
}
This is not a working code. Im having problems with LINQ and the inner joins. Could someone please help me to get the correct strukture. I hope its kinda clear what im trying to achive.
Best Regards
Marthin
var result =
new XDocument(new XElement("root",
from dt in dataTypes
join dp in dataPoints on dt.Id equals dp.RefId
select new XElement("type",
new XElement("name", dt.Name),
new XElement("data",
new XElement("date", dp.PointDate),
new XElement("temp", dp.PointValue)))));
var result =
new XDocument(new XElement("root",
from dt in dataTypes
select new XElement("type",
new XElement("name", dt.Name),
from dp in dataPoints
where dp.RefId == dt.Id
select new XElement("data",
new XElement("date", dp.PointDate),
new XElement("temp", dp.PointValue)))));

Categories

Resources