Linq to XML query returining a list of strings - c#

I have the following class
public class CountrySpecificPIIEntity
{
public string Country { get; set; }
public string CreditCardType { get; set; }
public String Language { get; set; }
public List<String> PIIData { get; set; }
}
I have the following XML that I want to query using Linq-to-xml and shape in to the class above
<?xml version="1.0" encoding="utf-8" ?>
<piisettings>
<piifilter country="DE" creditcardype="37" language="ALL" >
<filters>
<filter>FIRSTNAME</filter>
<filter>SURNAME</filter>
<filter>STREET</filter>
<filter>ADDITIONALADDRESSINFO</filter>
<filter>ZIP</filter>
<filter>CITY</filter>
<filter>STATE</filter>
<filter>EMAIL</filter>
</filters>
</piifilter>
<piifilter country="DE" creditcardype="37" Language="en" >
<filters>
<filter>EMAIL</filter>
</filters>
</piifilter>
</piisettings>
I'm using the following query but I'm having trouble with the last attribute i.e. PIIList.
var query = from pii in xmlDoc.Descendants("piifilter")
select new CountrySpecificPIIEntity
{
Country = pii.Attribut("country").Value,
CreditCardType = pii.Attribute("creditcardype").Value,
Language = pii.Attribute("Language").Value,
PIIList = (List<string>)pii.Elements("filters")
};
foreach (var entity in query)
{
Debug.Write(entity.Country);
Debug.Write(entity.CreditCardType);
Debug.Write(entity.Language);
Debug.Write(entity.PIIList);
}
How Can I get the query to return a List to be hydrated in to the PIIList property.

var query = from pii in xmlDoc.Descendants("piifilter")
select new CountrySpecificPIIEntity
{
Country = pii.Attribut("country").Value,
CreditCardType = pii.Attribute("creditcardype").Value,
Language = pii.Attribute("Language").Value,
PIIList = pii.Elements("filters").Select(xe => xe.Value).ToList();
};

i am not sure, can you check you class definition once to check if ?
public List<String> PIIList { get; set; }
if this is correct then try this:
select new CountrySpecificPIIEntity
{
Country = pii.Attribute("country").Value,
CreditCardType = pii.Attribute("creditcardype").Value,
Language = pii.Attribute("Language").Value,
PIIList = pii.Elements("filters").Cast<string>().ToList()
}

Related

Read XML to linq object, then create XML

I have the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<pp010 xmlns="http://www.123456768.com/technology">
<rptHdr>
<exchNam>MyXML</exchNam>
<envText>P</envText>
<rptCod>pp010</rptCod>
<rptNam>Daily Stock</rptNam>
<membLglNam>CompanyA</membLglNam>
<rptPrntEffDat>2015-04-14</rptPrntEffDat>
<rptPrntRunDat>2015-04-14</rptPrntRunDat>
</rptHdr>
<pp010Grp>
<pp010KeyGrp>
<membClgIdCod>HBGKP</membClgIdCod>
</pp010KeyGrp>
<pp010Grp1>
<pp010KeyGrp1>
<membExchIdCod>JBGJG</membExchIdCod>
</pp010KeyGrp1>
<pp010Grp2>
<pp010KeyGrp2>
<currTypCod>CHF</currTypCod>
</pp010KeyGrp2>
<pp010Grp3>
<pp010KeyGrp3>
<acctTypGrp>PP</acctTypGrp>
</pp010KeyGrp3>
<pp010Rec>
<mgnGrpCod> </mgnGrpCod>
<mgnClsCod>CSLN </mgnClsCod>
<mgnPremiumAmnt>+222926.00</mgnPremiumAmnt>
<mgnLiqDlvAmnt>+0.00</mgnLiqDlvAmnt>
<mgnSprdAmnt>+0.00</mgnSprdAmnt>
<mgnAddlAmnt>+89349.30</mgnAddlAmnt>
<unadjMgnReq>+312275.30</unadjMgnReq>
</pp010Rec>
<pp010Rec>
<mgnGrpCod> </mgnGrpCod>
<mgnClsCod>CSLM </mgnClsCod>
<mgnPremiumAmnt>+55112.00</mgnPremiumAmnt>
<mgnLiqDlvAmnt>+0.00</mgnLiqDlvAmnt>
<mgnSprdAmnt>+0.00</mgnSprdAmnt>
<mgnAddlAmnt>+30854.40</mgnAddlAmnt>
<unadjMgnReq>+85966.40</unadjMgnReq>
</pp010Rec>
I am using the following code but cannot seem to create an IEnumberable with the data from ...
public class MarginRep
{
public string mgnGrpCod {get;set;}
public string mgnClsCod {get;set;}
public string mgnPremiumAmnt {get;set;}
public string mgnLiqDlvAmnt {get;set;}
public string mgnSprdAmnt {get;set;}
public string mgnAddlAmnt {get;set;}
public string unadjMgnReq {get;set;}
}
private void button1_Click(object sender, EventArgs e)
{
string file = #"D:\WorkDesktop\VisualStudio\file.xml";
XDocument xmlDoc = XDocument.Load(file);
IEnumerable<MarginRep> myMarginRep =
from c in xmlDoc.Descendants("pp010Rec")
select new MarginRep()
{
mgnGrpCod = (string)c.Attribute("mgnGrpCod"),
mgnClsCod = (string)c.Attribute("mgnClsCod"),
mgnPremiumAmnt = (string)c.Attribute("mgnPremiumAmnt"),
mgnLiqDlvAmnt = (string)c.Attribute("mgnLiqDlvAmnt"),
mgnSprdAmnt = (string)c.Attribute("mgnSprdAmnt"),
mgnAddlAmnt = (string)c.Attribute("mgnAddlAmnt"),
unadjMgnReq = (string)c.Attribute("unadjMgnReq"),
};
}
Sorry for the large amount of the XML. I felt i needed to display it as the first couple of lines i dont need and cannot seem to navigate down the pp010Rec
IF you can offer any help I would be grateful, if you can point me into the direction literature I can spend time reading and try it alone.
Cheers,
It seems you want the values not the attributes, so change:
mgnGrpCod = (string)c.Attribute("mgnGrpCod"),
To:
mgnGrpCod = (string)c.Element("mgnGrpCod").Value,
Do the same with the other properties
I a bit changed .xml:
<?xml version="1.0" encoding="UTF-8"?>
<pp010 xmlns="http://www.123456768.com/technology">
<rptHdr>
<exchNam>MyXML</exchNam>
<envText>P</envText>
<rptCod>pp010</rptCod>
<rptNam>Daily Stock</rptNam>
<membLglNam>CompanyA</membLglNam>
<rptPrntEffDat>2015-04-14</rptPrntEffDat>
<rptPrntRunDat>2015-04-14</rptPrntRunDat>
</rptHdr>
<pp010Grp>
<pp0510KeyGrp>
<membClgIdCod>HBGKP</membClgIdCod>
</pp0510KeyGrp>
<pp010Grp1>
<pp010KeyGrp1>
<membExchIdCod>JBGJG</membExchIdCod>
</pp010KeyGrp1>
<pp010Grp2>
<pp010KeyGrp2>
<currTypCod>CHF</currTypCod>
</pp010KeyGrp2>
<pp010Grp3>
<pp010KeyGrp3>
<acctTypGrp>PP</acctTypGrp>
</pp010KeyGrp3>
<pp010Rec>
<mgnGrpCod> </mgnGrpCod>
<mgnClsCod>CSLN </mgnClsCod>
<mgnPremiumAmnt>+222926.00</mgnPremiumAmnt>
<mgnLiqDlvAmnt>+0.00</mgnLiqDlvAmnt>
<mgnSprdAmnt>+0.00</mgnSprdAmnt>
<mgnAddlAmnt>+89349.30</mgnAddlAmnt>
<unadjMgnReq>+312275.30</unadjMgnReq>
</pp010Rec>
<pp010Rec>
<mgnGrpCod> </mgnGrpCod>
<mgnClsCod>CSLM </mgnClsCod>
<mgnPremiumAmnt>+55112.00</mgnPremiumAmnt>
<mgnLiqDlvAmnt>+0.00</mgnLiqDlvAmnt>
<mgnSprdAmnt>+0.00</mgnSprdAmnt>
<mgnAddlAmnt>+30854.40</mgnAddlAmnt>
<unadjMgnReq>+85966.40</unadjMgnReq>
</pp010Rec>
</pp010Grp3>
</pp010Grp2>
</pp010Grp1>
</pp010Grp>
</pp010>
So you can run
public class MarginRep
{
public string mgnGrpCod { get; set; }
public string mgnClsCod { get; set; }
public string mgnPremiumAmnt { get; set; }
public string mgnLiqDlvAmnt { get; set; }
public string mgnSprdAmnt { get; set; }
public string mgnAddlAmnt { get; set; }
public string unadjMgnReq { get; set; }
public override string ToString()
{
return string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}",
mgnGrpCod, mgnClsCod, mgnPremiumAmnt, mgnLiqDlvAmnt, mgnSprdAmnt,
mgnAddlAmnt, unadjMgnReq);
}
}
var xmlDoc = XDocument.Load("1.xml");
XNamespace xn = xmlDoc.Root.Name.Namespace;
IEnumerable<MarginRep> myMarginRep = xmlDoc.Root.Descendants(xn + "pp010Rec")
.Select(c => new MarginRep()
{
mgnGrpCod = c.Element(xn + "mgnGrpCod").Value,
mgnClsCod = c.Element(xn + "mgnClsCod").Value,
mgnPremiumAmnt = c.Element(xn + "mgnPremiumAmnt").Value,
mgnLiqDlvAmnt = c.Element(xn + "mgnLiqDlvAmnt").Value,
mgnSprdAmnt = c.Element(xn + "mgnSprdAmnt").Value,
mgnAddlAmnt = c.Element(xn + "mgnAddlAmnt").Value,
unadjMgnReq = c.Element(xn + "unadjMgnReq").Value
});
foreach (var x in myMarginRep)
Console.WriteLine(x);
Print:
CSLN
+222926.00
+0.00
+0.00
+89349.30
+312275.30
CSLM
+55112.00
+0.00
+0.00
+30854.40
+85966.40
Update:
Link: http://rextester.com/UFLPQ70590
You do need to do what JAT said in his post about changing the attributes to elements, but that isn't the reason why your list is not being created from the XML. It's the namespace ("xmlns="http://www.123456768.com/technology") that is giving you trouble. I'm not sure what your XML file is trying to accomplish by having it, but if you remove it and do what JAT recommended your IEnumerable will start to be populated.
For more information about namespaces, you can check out this link from w3:
http://www.w3schools.com/xml/xml_namespaces.asp

MongoDB Unable to determine the serialization information for the expression error

My data is having following structure
public enum ParamType
{
Integer=1,
String=2,
Boolean=3,
Double=4
}
public class Gateway
{
public int _id { get; set; }
public string SerialNumber { get; set; }
public List<Device> Devices { get; set; }
}
public class Device
{
public string DeviceName { get; set; }
public List<Parameter> Parameters { get; set; }
}
public class Parameter
{
public string ParamName { get; set; }
public ParamType ParamType { get; set; }
public string Value { get; set; }
}
I filled 10 document objects of Gateway in a MongoDB database.
Now I want to query all those gateways which contains a device having Parameter with ParamName as "Target Temperature" and whose Value > 15.
I created following queries
var parameterQuery = Query.And(Query<Parameter>.EQ(p => p.ParamName, "Target Temperature"), Query<Parameter>.GT(p => int.Parse(p.Value), 15));
var deviceQuery = Query<Device>.ElemMatch(d => d.Parameters, builder => parameterQuery);
var finalQuery = Query<Gateway>.ElemMatch(g => g.Devices, builder => deviceQuery);
But when I run this, it is giving an exception
Unable to determine the serialization information for the expression: (Parameter p) => Int32.Parse(p.Value)
Please suggest where I am wrong.
As the error suggests, you can't use Int32.Parse inside your query. This lambda expression is used to get out the name of the property and it doesn't understand what Int32.Parse is.
If you are querying a string, you need to use a string value for comparison:
var parameterQuery = Query.And(Query<Parameter>.EQ(p => p.ParamName, "Target Temperature"), Query<Parameter>.GT(p => p.Value, "15"));
However, that's probably not what you want to do since you're using GT. To be treated as a number for this comparison you need the value to actually be an int in mongo, so you would need to change the type of your property:
public class Parameter
{
public string ParamName { get; set; }
public ParamType ParamType { get; set; }
public int Value { get; set; }
}
var parameterQuery = Query.And(Query<Parameter>.EQ(p => p.ParamName, "Target Temperature"), Query<Parameter>.GT(p => p.Value, 15));
Summary
I ran into this when I was modifying a list. It appears that Linq First/FirstOrDefault was not handled well by MongoDB for me. I changed to be an array index var update = Builders<Movie>.Update.Set(movie => movie.Movies[0].MovieName, "Star Wars: A New Hope"); Note: This is in Asp.Net 5 using MongoDB.Driver 2.2.0.
Full Example
public static void TypedUpdateExample() {
var client = new MongoClient("mongodb://localhost:27017");
var database = client.GetDatabase("test");
var collection = database.GetCollection<Movie>("samples");
//Create some sample data
var movies = new Movie {
Name = "TJ",
Movies = new List<MovieData>
{
new MovieData {
MovieName = "Star Wars: The force awakens"
}
}
};
collection.InsertOne(movies);
//create a filter to retreive the sample data
var filter = Builders<Movie>.Filter.Eq("_id", movies.Id);
//var update = Builders<Movie>.Update.Set("name", "A Different Name");
//TODO:LP:TSTUDE:Check for empty movies
var update = Builders<Movie>.Update.Set(movie => movie.Movies[0].MovieName, "Star Wars: A New Hope");
collection.UpdateOne(filter, update);
}
public class Movie {
[BsonId]
public ObjectId Id { get; set; }
public string Name { get; set; }
public List<MovieData> Movies { get; set; }
}
public class MovieData {
[BsonId]
public ObjectId Id { get; set; }
public string MovieName { get; set; }
}

How to parse XML data into the properties of a custom C# class?

Setup
I have this in my Main() function.
List<Token> tokens = new List<Token>();
string path = #"\(some directories)\tokens.xml";
XDocument doc = XDocument.Load(path);
I have this class with a few properties.
public partial class Token
{
public Token()
{
SetURLs = new List<string>();
SetNames = new List<string>();
}
public string Name { get; set; }
public List<string> SetURLs { get; set; }
public List<string> SetNames { get; set; }
public string Color { get; set; }
public string PT { get; set; }
public string Text { get; set; }
}
I have this XML file. Here is a snippet.
<?xml version="1.0" encoding="UTF-8"?> //EDIT3
<card_database version="2"> //EDIT3
<cards>
.
.
.
<card>
<name>Griffin</name>
<set picURL="http://magiccards.info/extras/token/duel-decks-ajani-vs-nicol-bolas/griffin.jpg" picURLHq="" picURLSt="">DDH</set>
<color>w</color>
<manacost></manacost>
<type>Token</type>
<pt>2/2</pt>
<tablerow>0</tablerow>
<text>Flying</text>
<token>1</token>
</card>
<card>
<name>Rat</name>
<set picURL="http://magiccards.info/extras/token/shadowmoor/rat.jpg" picURLHq="" picURLSt="">SHM</set>
<set picURL="http://magiccards.info/extras/token/gatecrash/rat.jpg" picURLHq="" picURLSt="">GTC</set>
<color>b</color>
<manacost></manacost>
<type>Token</type>
<pt>1/1</pt>
<tablerow>0</tablerow>
<text></text>
<token>1</token>
</card>
.
.
.
</cards>
</card_database> //EDIT3
As you can see, there are many <card> elements in the <cards> root element. Further, each <card> can have many <set> elements. I made the class accordingly.
Problem
How do I go through one <card> at a time and assign the appropriate values to each property?
What I Have Tried
I made a list that contains all of the <name> elements of each <card>. I would then go through this list and assign a name to a new instance of the Token class's Name property. Then populate my tokens list with each new instance.
List<string> names = new List<string>();
names = doc.Descendants("card").Elements("name").Select(r => r.Value).ToList();
int amount = doc.Descendants("card").Count();
for(int i = 0; i < amount; i++)
{
Token token = new Token();
token.Name = name[i];
.
.
.
tokens.Add(token);
}
I guess I could then make more lists that contain every other desired element and do the same process, but there has to be a more elegant way, right?
EDIT
I also tried serialization from another question. But for some reason, when I tried to write something from token to the console (say token.Name), it didn't write anything.
XmlSerializer serializer = new XmlSerializer(typeof(Token));
using (StringReader reader = new StringReader(path))
{
Token token = (Token)(serializer.Deserialize(reader));
}
Probably just an incorrect implementation. If that is the case, could someone use what I posted and show me the correct implementation? Also, I assume this will give 1 or many values to my two List properties, right?
EDIT
Thanks for the help.
EDIT2
An Answer
After some unsuccessful fiddling with serialization and some more searching, I made an implementation that works.
foreach (var card in doc.Descendants("card"))
{
Token token = new Token();
token.Name = card.Element("name").Value.ToString();
foreach (var set in card.Elements("set"))
{
token.SetURLs.Add(set.Attribute("picURL").Value.ToString());
token.SetNames.Add(set.Value.ToString());
}
token.Color = card.Element("color").Value.ToString();
token.PT = card.Element("pt").Value.ToString();
token.Text = card.Element("text").Value.ToString();
tokens.Add(token);
}
Much better than the number of Lists I first had in mind. Not as succinct as the serialization might have been. However, it does what I need.
Thanks for the help.
EDIT4
Not sure if this many edits are allowed or against etiquette. Just wanted to make this edit for future readers.
The stuff under the "An Answer" section does solve my problem but the XML Serialization posted below by Dave is much better; it is more flexible and easier to reuse/modify. So, pick the solution that has more benefits for your situation.
Using XML Serialization I was able to deserialize your snippet into some objects. I don't really understand your 2 different list variables so i modified it into 1 list.
I am not sure if this is exactly what you are trying to pull off, but i believe it should help you with your xml deserialization of multiple "set" elements.
I created a file with your same snippet named tokens.xml, edited to match your new layout.
<?xml version="1.0" encoding="UTF-8"?>
<card_database version="2">
<cards>
<card>
<name>Griffin</name>
<set picURL="http://magiccards.info/extras/token/duel-decks-ajani-vs-nicol-bolas/griffin.jpg" picURLHq="" picURLSt="">DDH</set>
<color>w</color>
<manacost></manacost>
<type>Token</type>
<pt>2/2</pt>
<tablerow>0</tablerow>
<text>Flying</text>
<token>1</token>
</card>
<card>
<name>Rat</name>
<set picURL="http://magiccards.info/extras/token/shadowmoor/rat.jpg" picURLHq="" picURLSt="">SHM</set>
<set picURL="http://magiccards.info/extras/token/gatecrash/rat.jpg" picURLHq="" picURLSt="">GTC</set>
<color>b</color>
<manacost></manacost>
<type>Token</type>
<pt>1/1</pt>
<tablerow>0</tablerow>
<text></text>
<token>1</token>
</card>
</cards>
</card_database>
I created a few classes
[XmlRoot(ElementName = "card_database")]
public class CardsDatabase
{
public CardsDatabase()
{
}
[XmlElement(ElementName = "cards", Form = XmlSchemaForm.Unqualified)]
public CardsList Cards { get; set; }
[XmlAttribute(AttributeName = "version", Form = XmlSchemaForm.Unqualified)]
public string Version { get; set; }
}
[XmlRoot(ElementName = "cards")]
public class CardsList
{
public CardsList()
{
Cards = new List<Card>();
}
[XmlElement(ElementName = "card", Form = XmlSchemaForm.Unqualified)]
public List<Card> Cards { get; set; }
}
[XmlRoot(ElementName = "card")]
public class Card
{
public Card()
{
SetURLs = new List<SetItem>();
}
[XmlElement(ElementName = "name", Form = XmlSchemaForm.Unqualified)]
public string Name { get; set; }
[XmlElement(ElementName = "set", Form = XmlSchemaForm.Unqualified)]
public List<SetItem> SetURLs { get; set; }
[XmlElement(ElementName = "color", Form = XmlSchemaForm.Unqualified)]
public string Color { get; set; }
[XmlElement(ElementName = "pt", Form = XmlSchemaForm.Unqualified)]
public string PT { get; set; }
[XmlElement(ElementName = "text", Form = XmlSchemaForm.Unqualified)]
public string Text { get; set; }
}
[XmlRoot(ElementName = "set")]
public class SetItem
{
public SetItem()
{
}
[XmlAttribute(AttributeName = "picURL", Form = XmlSchemaForm.Unqualified)]
public string PicURL { get; set; }
[XmlAttribute(AttributeName = "picURLHq", Form = XmlSchemaForm.Unqualified)]
public string PicURLHq { get; set; }
[XmlAttribute(AttributeName = "picURLSt", Form = XmlSchemaForm.Unqualified)]
public string PicURLSt { get; set; }
[XmlText]
public string Value { get; set; }
}
The main body is as follows (I know this is ugly, but i was going fast so please improve)
CardsDatabase cards = new CardsDatabase();
string path = #"tokens.xml";
XmlDocument doc = new XmlDocument();
doc.Load(path);
XmlSerializer serializer = new XmlSerializer(typeof(CardsDatabase));
using (StringReader reader = new StringReader(doc.InnerXml))
{
cards = (CardsDatabase)(serializer.Deserialize(reader));
}
The following is what the output looked like.
With Linq to Xml,
string path = #"~/tokens.xml";
var doc = XDocument.Load(Server.MapPath(Url.Content(path)));
var cards = doc.Descendants("card")
.Select(x =>
new Token
{
Name = x.Element("name").Value,
SetURLs = x.Elements("set").Select(y => y.Attribute("picURL").Value)
.ToList(),
SetNames = x.Elements("set").Select(y => y.Value).ToList(),
Color = x.Element("color").Value,
PT = x.Element("pt").Value,
Text = x.Element("text").Value
}).ToList();
hope this helps.
Check out this post:
XPath and *.csproj
But take the below and convert the anonymous types to your concrete class(es).
It should be enough to get you started.
XDocument xDoc = /* populate from somewhere */
XNamespace nsPlaceHolder = XNamespace.Get("http://schemas.microsoft.com/developer/msbuild/2003");
XNamespace ns = string.Empty;
var list1 = from list in xDoc.Descendants(ns + "cards")
from item in list.Elements(ns + "card")
/* where item.Element(ns + "card") != null */
select new
{
PicURL = item.Attribute("picURL").Value,
MyName = (item.Element(ns + "name") == null) ? string.Empty : item.Element(ns + "name").Value
};
foreach (var v in list1)
{
Console.WriteLine(v.ToString());
}

Read XML file with LINQ and create object with IEnumerable propety

Here is my problem:
I have this XML file:
<?xml version="1.0" encoding="utf-8" ?>
<settings>
<app name="Application1">
<log name="Log1" path="d:\paths\" filename="Log1File"/>
<log name="Log2" path="d:\paths\"/>
<log name="log3" path="d:\paths\" filename="Log3File"/>
</app>
</settings>
And I'm trying to read it with LINQ and create object of this class:
public class Apps
{
public string Name { get; set; }
public IEnumerable<Logs> Logs { get; set; }
}
public class Logs
{
public string Name { get; set; }
public string Path { get; set; }
public string Filename { get; set; }
}
So far I managed to create this bit of code however looks like it only gets first log element mean time I need all log elements for each app element:
public static IEnumerable<Apps> GetAllApps()
{
var items = from a in db.Descendants("app")
orderby a.Attribute("name").Value
select new Apps
{
Name = a.Attribute("name").Value,
Logs = from b in a.Descendants("log")
select new Logs
{
Name = b.Attribute("name").Value,
Path = b.Attribute("path").Value,
Filename = b.Attribute("filename").Value
}
};
return items;
}
I would use serialization here
XmlSerializer ser = new XmlSerializer(typeof(Settings));
var result = (Settings)ser.Deserialize(stream);
[XmlRoot("settings")]
public class Settings
{
[XmlElement("app")]
public Apps[] apps;
}
public class Apps
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlElement("log")]
public Logs[] Logs { get; set; }
}
public class Logs
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlAttribute("path")]
public string Path { get; set; }
[XmlAttribute("filename")]
public string Filename { get; set; }
}
I used fluent API, but let you adapt as you prefer...
Problem is a NullReferenceException, as one of your Logs in xml has no "filename" attribute. And when you use "Value" on a null, you get a NRE.
So, check if the Attribute exists before trying to get it's value.
var it = db.Descendants("app")
.OrderBy(app => app.Attribute("name").Value)
.Select(app => new Apps() {
Name = app.Attribute("name").Value,
Logs = app.Descendants("log").Select(a =>
new Logs() {
Name = a.Attribute("name") != null ? a.Attribute("name").Value : null,
Path = a.Attribute("path") != null ? a.Attribute("path").Value : null,
Filename = a.Attribute("filename") != null ? a.Attribute("filename").Value : null
}).ToList()
}).ToList();

How to load XML Elements using LINQ from XDocument into a class (not using Descendants)

How to load XML Elements using LINQ from XDocument into a c# class. I don't want to use XDocument.Descendants because the settings are not repeated and occur only once in the XML.
This works but I think there must be another way where I don't have to use IEnumerable or use ToArray() and use the first element [0]. Any suggestions?
CODE
public class BackupItem // class needed so LINQ reads XML into an editable DbGrid
{
public bool Backup { get; set; }
public bool IncludeSubDir { get; set; }
public string BackupLabel { get; set; }
public string BackupPath { get; set; }
}
public class BackupSettings
{
public bool IncludeDateStamp { get; set; }
public bool DatePrefix { get; set; }
public bool DateSuffix { get; set; }
public string DateFormat { get; set; }
public bool ZipCompress { get; set; }
public bool ZipPrefix { get; set; }
public bool ZipSuffix { get; set; }
public string ZipText { get; set; }
public bool BackupToSiblingFolder { get; set; }
public string SiblingFolder { get; set; }
public bool BackupToFolder { get; set; }
public bool IncludeFullPath { get; set; }
public string BackupFolder { get; set; }
}
// use a LINQ query to load xml file into datagridview and make datagrid editable (create class with get/set)
var q = from arg in GvXMLDoc.Descendants("BackupItem")
select new BackupItem()
{
Backup = (bool)arg.Element("IncludeDirectory"),
IncludeSubDir = (bool)arg.Element("IncludeSubDirectories"),
BackupLabel = (string)arg.Element("BackupLabel"),
BackupPath = (string)arg.Element("BackupPath")
};
dataGridView1.DataSource = q.ToList();
// load global variable
IEnumerable<BackupSettings> GvBackupSettings = from arg in GvXMLDoc.Element("Backup").Elements("Settings")
select new BackupSettings()
{
IncludeDateStamp = (bool)arg.Element("IncludeDateStamp"),
DatePrefix = (bool)arg.Element("DatePrefix"),
DateSuffix = (bool)arg.Element("DateSuffix"),
DateFormat = (string)arg.Element("DateFormat"),
ZipCompress = (bool)arg.Element("ZipCompress"),
ZipPrefix = (bool)arg.Element("ZipPrefix"),
ZipSuffix = (bool)arg.Element("ZipSuffix"),
ZipText = (string)arg.Element("ZipText"),
BackupToSiblingFolder = (bool)arg.Element("BackupToSiblingFolder"),
SiblingFolder = (string)arg.Element("SiblingFolder"),
BackupToFolder = (bool)arg.Element("BackupToFolder"),
IncludeFullPath = (bool)arg.Element("IncludeFullPath"),
BackupFolder = (string)arg.Element("BackupFolder")
};
I can access the values but it seems a very 'messy way'.
var s = GvBackupSettings.ToArray()[0].DateSuffix;
var t = GvBackupSettings.ToArray()[0].DateFormat;
XML FILE
<?xml version='1.0'?>
<Backup>
<Settings>
<IncludeDateStamp>true</IncludeDateStamp>
<DatePrefix>false</DatePrefix>
<DateSuffix>true</DateSuffix>
<DateFormat>yyyy-MM-dd h.m.s</DateFormat>
<ZipCompress>false</ZipCompress>
<ZipPrefix>false</ZipPrefix>
<ZipSuffix>false</ZipSuffix>
<ZipText></ZipText>
<BackupToSiblingFolder>true</BackupToSiblingFolder>
<SiblingFolder>backups</SiblingFolder>
<BackupToFolder>false</BackupToFolder>
<IncludeFullPath>true</IncludeFullPath>
<BackupFolder>C:\\backup</BackupFolder>
</Settings>
<BackupItem>
<IncludeDirectory>true</IncludeDirectory>
<IncludeSubDirectories>true</IncludeSubDirectories>
<BackupLabel>Backup1</BackupLabel>
<BackupPath>C:\TestFiles\Xml\Samples</BackupPath>
</BackupItem>
<BackupItem>
<IncludeDirectory>true</IncludeDirectory>
<IncludeSubDirectories>false</IncludeSubDirectories>
<BackupLabel>Backup2</BackupLabel>
<BackupPath>C:\TestFiles\Xml\Samples</BackupPath>
</BackupItem>
</Backup>
EDIT 1
I also am trying to deserialize the XML (thanks for idea) so I don't have to cast each item. This does not work.. I don't want to have to run the XSD tool either and have a huge class file...
XmlRootAttribute rootAttribute = new XmlRootAttribute("Backup");
XmlSerializer deserializer = new XmlSerializer((typeof(BackupSettings)), rootAttribute);
GvBackupSettings = (BackupSettings)deserializer.Deserialize(XmlReader.Create(GvXMLFileName));
EDIT 2
Ended up serializing and deserializing xml as suggested below using stardard XSD.exe tool to generate the c# class. Especially as I had to read and write to same xml file. Note: Make sure you verify/modify the generated xsd file first.
You don't need a query if you just need the first element:
XElement settings = GvXMLDoc.Element("Backup").Element("Settings");
BackupSettings GvBackupSettings = new BackupSettings
{
IncludeDateStamp = (bool)settings.Element("IncludeDateStamp"),
DatePrefix = (bool)settings.Element("DatePrefix"),
...
};
var s = GvBackupSettings.DateSuffix;
var t = GvBackupSettings.DateFormat;

Categories