I have a specific function that I want to run, and it is located inside an XML File:
Console.WriteLine("Text for test, {0}, {1}", testWord, testWord2);
The text is stored in an XML file:
<?xml version="1.0" encoding="utf-8" ?>
<root>
<world>
<region name="TestRegion">
<area name="TestArea">
<building name="Outside">
<room name="TutorialRoom">
<textToDisplay>"Text for test, {0},{1}"</textToDisplay>
<extraString>testWord,tesWord2</extraString>
</room>
</building>
</area>
</region>
</world>
</root>
I can easily get the string data using LINQ
XElement xelement = XElement.Load("..\\..\\LocationDatabase.xml");
var textToDisplay= xelement.Elements("world")
.Elements("region").Where(region => (string)region.Attribute("name") == "TestRegion")
.Elements("area").Where(area => (string)area.Attribute("name") == "TestArea")
.Elements("building").Where(building => (string)building.Attribute("name") == "Outside")
.Elements("room").Where(room => (string)room.Attribute("name") == "TutorialRoom")
.Elements("textToDisplay");
var extraString= xelement.Elements("world")
.Elements("region").Where(region => (string)region.Attribute("name") == "TestRegion")
.Elements("area").Where(area => (string)area.Attribute("name") == "TestArea")
.Elements("building").Where(building => (string)building.Attribute("name") == "Outside")
.Elements("room").Where(room => (string)room.Attribute("name") == "TutorialRoom")
.Elements("extraString");
My issue here is how I make the command `Console.WriteLine("Something {0}", extra); to accept both LINQ statements. Anybody have an idea?
You can simply just pass in the strings as they are, however you will probably need to split the second one at the comma.
string one = "format me {0}{1}";
string two = "here, and here";
Console.WriteLine(one, two.Split(','));
IDEOne Example
The call to Console.WriteLine() is nothing special -- it just expects a string and an object[]. Your data is there, but buried in XElement instances. So
Console.WriteLine(
(string) textToDisplay.First(),
((string) extraString.First()).Split(',')
);
does what you want.
Note that this only works when "extraString" is something simple like a comma-separated list. You can't put arbitrary expressions like "2 + 2" in there and expect that to work -- for that we'd need to invoke the compiler and actually produce code, which is much more complicated.
how about this:
First split the extra string into a String[] by using Split(',') and then use a String.Format() to display the textToDisplay with the 2 strings in your string array.
var values = extraString.FirstOrDefault().Value.Split(',');
Console.WriteLine(String.Format(textToDisplay.First().Value, values[0], values[1]));
Related
I have the following XML fragment and would like to pull out the values of the status attributes that are not zero. I can obtain the elements that match the criteria but what I really want is the values of the status attributes.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response>
<Auth status = "0">Moo</Auth>
<Add status = "817">Cow</Add>
<Add status = "888">Brown</Add>
<Add status = "123">Dog</Add>
</response>
This lambda syntax brings back a list of the matching elements but what I need is a list of the status values not a list of the elements with those values.
var errcodeList = xml.Descendants("Add").Where(x => x.Attribute("status").Value != "0").Attributes("status");
When you use Select, you are projecting the IEnumerable into another form, in this case it's a list of x.Attributes("status").Value
var errcodeList = xml.Descendants("Add")
.Where(x => x.Attribute("status").Value != "0")
.Select(x => x.Attributes("status").Value);
You can use Select to project the collection to the specific results that you want. For example:
var errcodeList = xml.Descendants("Add").Where(x => x.Attribute("status").Value != "0").Select(x => x.Attribute("status").Value);
I have the following chunk of XML code that I can easily generate.
<?xml version="1.0" encoding="utf-8"?>
<sessions>
<session date="14.10.2016" time="0:1" amount="1">
<Folder>C:\Users</Folder>
<Folder>C:\Tes2t</Folder>
<Folder>C:\Asgbsf\Aleksei</Folder>
</session>
<session date="14.10.2016" time="15:40" amount="7">
<Folder>C:\Users</Folder>
<Folder>C:\Tes2taaaa</Folder>
<Folder>C:\Asgbsf\Aleksei</Folder>
</session>
</sessions>
I am searching for data with attribute time 15:40 and date 14.10.2016 using following function
private static IEnumerable<XElement> FindElements(string filename, string date, string time)
{
XElement x = XElement.Load(filename);
return x.Descendants().Where(e => e.Attributes("date").Any(a => a.Value.Equals(date)) &&
e.Attributes("time").Any(a => a.Value.Equals(time)));
}
Function being executed like:
foreach (XElement x in FindElements(pathToXml, "14.10.2016", "15:40"))
Console.WriteLine(x.ToString());
Everything is fine, but the output is
<session date="14.10.2016" time="15:40" amount="7">
<Folder>C:\Users</Folder>
<Folder>C:\Tes2taaaa</Folder>
<Folder>C:\Asgbsf\Aleksei</Folder>
</session>
And I need just the folders, eg.
<Folder>C:\Users</Folder>
<Folder>C:\Tes2taaaa</Folder>
<Folder>C:\Asgbsf\Aleksei</Folder>
How do I achieve this? Help please.
(It seems that I am a little bit late, but..) in some cases like this using Xpath is easier than Linq .
var folders = XDocument.Load(filename)
.XPathSelectElements("//session[#date='14.10.2016' and #time='15:40']/Folder");
You are currently returning the Element that has attribute of date and time with these values. What you should add to it is to return its child elements of Folder. You can do this by adding .Elements("Folder") after the .Where.
However, I think you can write your query a bit nicer:
Look for all the sessions that the values of those attriute equal to the given input. Then return the element.Elements("Folder").
I've added the .SelectMany() to flatten the inner list of child elements
string date = "14.10.2016";
string time = "15:40";
var result = (from element in XDocument.Load("data.xml").Descendants("session")
where element.Attribute("date")?.Value == date &&
element.Attribute("time")?.Value == time
select element.Elements("Folder")).SelectMany(item => item).ToList();
Some time ago i asked the question how to read from a xml document and use the value (see C# xdocument read from element and put the value into string)
Now i have the following issue, the solution given in the last thread works, but only if i do this:
<root>
<test>
<Copy01>#SRCDIR#\test1 ,#INSTDIR#\test2</Copy01>
</test>
</root>
but i want something like this:
<root>
<test>
<Copy01>#SRCDIR#\test1 ,#INSTDIR#\test2</Copy01>
<Copy02>#SRCDIR#\test3 ,#INSTDIR#\test4</Copy02>
</test>
</root
but with the following code in C#:
var copyitems = doc.Descendants(param[1])
.Select(s =>
{
var splits = s.Value.Split(new string[] { "#SRCDIR#", "#INSTDIR#" }, StringSplitOptions.RemoveEmptyEntries); // split the string to separate source and destination.
return new { Source = splits[0].Replace(",", ""), Destination = splits[1].Replace(",", "") };
})
.ToList();
Value of param[1] is "test" in this case
it only picks the first copy (copy01) and not the second one.
Any idea how to fix this?
Nick
You seem to want to select the child elements of the test elements. You can use SelectMany and the Elements methods to do it like this:
var copyitems =
doc.Descendants("test") //Select all "test" elements
.SelectMany(x => x.Elements()) //Select all children of all "test" elements
.Select(s =>
{
//...
})
.ToList();
I have a LINQ expression which gets the XML attribute values from a xml file.
var xml = XElement.Load(#"C:\\StoreServer1.xml");
var query = from e in xml.Descendants("Groups")
where int.Parse(e.Element("Store").Value) == 1500
select e.Element("Store").Attribute("WeekDayStClose").Value;
And the xml file is:
enter<?xml version="1.0" encoding="utf-8" ?>
<Stores>
<Groups Range="000">
<Store WeekDayStClose="210" SatStClose="21" SunStClose="22">1500</Store>
<Store WeekDayStClose="23" SatStClose="24" SunStClose="25">18</Store>
<Store WeekDayStClose="23" SatStClose="24" SunStClose="25">19</Store>
</Groups>
</Stores>
I am only getting the attribute result (value) for first element of 1500. If I search same thing for 18 it doesn't return any result and no exception. Any help appreciated....Plz help!!!
Try this out:-
var xml = XElement.Load(#"C:\\StoreServer1.xml");
var query = xml.Descendants("Groups").Descendants("Store").Where(e => int.Parse(e.Value) == 18).Select(e=> e.Attribute("WeekDayStClose").Value);
You should be more granular, call sub Descendants with Store (XName):
var xml = XElement.Load(#"C:\\New Folder\\StoreServer1.xml");
var query = from e in xml.Descendants("Groups").Descendants("Store")
where int.Parse(e.Value) == 18
select e.Attribute("WeekDayStClose").Value;
Because now you're retrieving only the first Store of each Group which is 1500.
Yes, you have a little error in your code:
You are splitting your xml into group-elements (you have just one group). Then you check if the first store element has the value 1500 (you are not checking if the following store elements have maybe the value 1500)
You need to change your code into the following
var query = from e in xml.Descendants("Store")
where int.Parse(e.Value) == 1500
select e.Attribute("WeekDayStClose").Value;
I'm working on a program that needs to be able to load object-properties from an XML file. These properties are configurable by the user and XML makes sense to me to use.
Take the following XML document.
<?xml version="1.0" encoding="utf-8" ?>
<udpcommands>
<command name="requser">
<cvar name="reqchallege" value="false" />
</command>
<command name="reqprocs">
<cvar name="reqchallenge" value="false" />
</command>
</udpcommands>
I need to be able to load values from the cvars above to properties. I'm think Linq-To-XML would be good for it (I'm looking for applications of Linq so I can learn it). I've got a Linq-to-XML query done to select the right "command" based on the name.I was reading MSDN for help on this.
The following code snippet goes in a constructor that takes the parameter "string name" which identifies the correct XML <command> to pull.
I would like to have one linq statement to pull each <cvar> out of that XML given the section name, dumping everything to an IEnumerable. Or, I'm looking for a better option perhaps. I'm open for anything really. I would just like to use Linq so I can learn it better.
XElement doc = XElement.Load("udpcommands.xml");
IEnumerable<XElement> a = from el in doc.Elements()
where el.FirstAttribute.Value == name
select el;
foreach (var c in a)
{
Console.WriteLine(c);
}
The above code snippet outputs the following to the console:
<command name="requser">
<cvar name="reqchallege" value="false" />
</command>
Something like this should do:
var result =
doc.Elements("command")
.Single( x => x.Attribute("name").Value == name)
.Elements("cvar");
This will give you an IEnumerable<XElement> where each XElement represents a cvar in the specified command.
Note that if the specified command does not exist, the call to Single will cause an error. Likewise if the specified attribute is not found on the command.
EDIT As per your comments, you could do something along the lines of:
// Result will be an XElement,
// or null if the command with the specified attribute is not found
var result =
doc.Elements("command")
// Note the extra condition below
.SingleOrDefault( x => x.Attribute("name")!=null && x.Attribute("name").Value == name)
if(result!=null)
{
// results.Elements() gives IEnumerable<XElement>
foreach(var cvar in results.Elements("cvar"))
{
var cvarName = cvar.Attribute("name").Value;
var cvarValue = Convert.ToBoolean( cvar.Attribute("value").Value );
}
}