I'm trying to sort the date-tags in my XML by value:
var noteElements = xDoc.Root.Descendants("Note").OrderBy(o => (DateTime)o.Element("date")).ToList();
foreach (XElement noteEl in noteElements)
{
string noteDateValue = noteEl.Element("date").Value;
noteEl.ReplaceWith(new XElement("notedate", noteEl, new XAttribute("date", noteDateValue)));
}
That doesn't work. The dates are not sorted as expected.
XML:
<Root>
<Notes>
<notedate date="date here"><Note>
<date>1997-07-04T00:00:00</date>
</Note></notedate>
<notedate date="date here"><Note>
<date>1997-06-04T00:00:00</date>
</Note></notedate>
</Notes>
</Root>
Anyone who can explain what I'm doing wrong?
You're replacing each Note element with a notedate element. The order in which you perform that replacement is irrelevant.
It sounds like you actually want something like:
var notes = doc.Root.Element("Notes");
notes.ReplaceNodes(notes.Elements()
.OrderBy(x => (DateTime) x.Element("date"))
.Select(x => new XElement("notedate",
new XAttribute("date", "date here"),
x));
Related
I am requesting some data from an API as XML. First to GET id's and then POST the id's to a second endpoint to get the data in question.
The problem I'm having is to extract the id's and put them in a List like this one
List<string>() { "idxxxx", "idxxxx", "idxxxx", "idxxxx" } for the POST request body.
GET response (xmlStringResponse) looks like this
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><TaxeringsenhetsreferensResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lmfault="http://namespace.lantmateriet.se/distribution/produkter/fault/v1" xmlns="http://namespace.lantmateriet.se/distribution/produkter/taxering/v2" xmlns:gml="http://www.opengis.net/gml/3.2"><Taxeringsenhetsreferens><id>553508-5</id><typ>Lantbruksenhet</typ></Taxeringsenhetsreferens><Taxeringsenhetsreferens><id>553511-5</id><typ>Specialenhet</typ></Taxeringsenhetsreferens><Taxeringsenhetsreferens><id>559405-5</id><typ>Industrienhet</typ></Taxeringsenhetsreferens><Taxeringsenhetsreferens><id>710531-4</id><typ>Industrienhet</typ></Taxeringsenhetsreferens></TaxeringsenhetsreferensResponse>
The code looks like this at this time and the idListXml is keep being empty. I'm doing something wrong here and cant figure it out..
XElement xmlTree = XElement.Parse(xmlStringResponse);
var idList = xmlTree.Descendants("Taxeringsenhetsreferens")
.Select(x => (string)x.Element("id"))
.ToList();
XElement requestBody = new XElement(ns + "IdRequest", idList.Select(l => new XElement(ns + "id", l)));
Thank you for your time!
Your code is good, you can just add the XNamespace, try the following code :
XElement xmlTree = XElement.Parse(xmlStringResponse);
XNamespace ns = "http://namespace.lantmateriet.se/distribution/produkter/taxering/v2";
var idList = xmlTree.Descendants(ns + "Taxeringsenhetsreferens")
.Select(x => x.Element(ns + "id").Value)
.ToList();
I hope this will help you get the expected result.
<test>
<acc id="1"> acc1 </acc>
<acc id="2"> acc2 </acc>
<acc id="3"> acc3 </acc>
<acc id="4"> acc4 </acc>
</test>
For example, if I want to take the value of each <acc> element:
var iAccs = xdoc.Descendants("test").Elements("acc").Select(p => p.Value);
List<string> myList = new List<string>();
foreach(string p in iAccs)
{
myList.Add(p);
}
But how to substract all the attribute "id" values of each <acc> elements?
You can easily get this using LINQ-to-XML:-
XDocument xdoc = XDocument.Load(#"You XML file path");
List<string> result = xdoc.Descendants("acc")
.Select(x => (string)x.Attribute("id")).ToList();
Or if you prefer query syntax then:-
List<int> result2 = (from x in xdoc.Descendants("acc")
select (int)x.Attribute("id")).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();
Good Day,
I am trying to query an XML document and have the following query:
XElement root = XElement.Load(#"..\..\Data.xml");
var entries = root.Descendants()
.Where(x => x.Name.LocalName == "Entry")
.ToList();
Console.WriteLine("There are {0} nodes...", entries.Count());
foreach (XElement v in entries)
{
Console.WriteLine(v.Value);
}
and this code works because it pulls the correct number of Entry nodes. The Entry
nodes look like:
<?xml version="1.0" encoding="UTF-8"?>
<Database xmlns="http://www.someurl.org/schemas">
<InfoFromRecord>
<BaseData>
<Entry>
<Date>2006-03-08</Date>XD
<Time>09:20:00</Time>
<EnteredBy>DNS</EnteredBy>
<TextEntry>Record 1</TextEntry>
</Entry>
<Entry>
<Date>2006-03-08</Date>
<Time>09:33:00</Time>
<EnteredBy>MW</EnteredBy>
<TextEntry>Record 2</TextEntry>
</Entry>
<Entry>
<Date>2006-03-08</Date>
<Time>08:58:00</Time>
<EnteredBy>BH</EnteredBy>
<TextEntry>Record 3</TextEntry>
</Entry>
</BaseData>
</InfoFromRecord>
</Database>
The problem is, I want to extract only the Date and Time, not all four fields.
Let's assume your entire XML file looks like this for a clear example:
<Entries>
<Entry>
<Date>2006-03-08</Date>
<Time>09:33:00</Time>
<EnteredBy>XX</EnteredBy>
<TextEntry>Test Data</TextEntry>
</Entry>
</Entries>
You could then do something like this:
var document = XDocument.Load(#"..\..\Data.xml");
var dateAndTimes =
from d in document.Root.Descendants("Entry")
select new
{
Date = d.Element("Date").Value,
Time = d.Element("Time").Value
};
From there, the dateAndTimes type will select an anonymous type of the Date and Time. You can change the anonymous type to be your own type, or something else.
EDIT: The problem is your xmlns. Change your code like so:
XNamespace namespc = "http://www.someurl.org/schemas";
var document = XDocument.Parse(xml);
var dateAndTimes =
from d in document.Root.Descendants(namespc + "Entry")
select new
{
Date = d.Element(namespc + "Date").Value,
Time = d.Element(namespc + "Time").Value
};
I haven't had a chance to try it but something like the following may give you what you are looking for
var entries = from i in root.Descendants()
where Name=='entry'
let date = i.Element('Date').Value
let time = i.ELement('Time').Value
select new Tuple<string,string>(date,time);
foreach (XElement v in entries)
{
Console.WriteLine(v.Element("Date").Value);
Console.WriteLine(v.Element("Time").Value);
}
Do not forget that Descendants finds children at any level, i.e. children, grand-children, etc where Elements find only direct child. So i guess Elements is the safe option in most of the cases.
EDIT : After seeing the XML
You need to include the Namspace also when getting the data
XNamespace ns = "http://www.someurl.org/schemas";
var entries = elm.Descendants().Where(x => x.Name.LocalName == "Entry").ToList();
foreach (XElement v in entries)
{
Console.WriteLine(v.Element(ns+"Date").Value);
Console.WriteLine(v.Element(ns+"Time").Value);
}
IEnumerable<XElement> de = from el in xdoc.Descendants() select el;
foreach (XElement el in de)
{
if (string.Equals(el.Name.ToString(), "movie",
`StringComparison.InvariantCultureIgnoreCase))`enter code here`
StringComparison.InvariantCultureIgnoreCase))
{
date(el);
}
I am returning a list. This is contains the names of xml nodes that cannot be blank in my XML file.
List<Setting> settingList = SettingsGateway.GetBySettingTypeList("VerifyField");
I have a LINQ Statement. I am trying to return all transactions that have empty nodes. The list here is returning the nodes that CANNOT be empty. Does anyone know what I am doing wrong here?
The following code is supposed to Bind the "transactions" to a DataGrid and display the Txn's that have empty nodes which are required.
var transactionList =
from transactions in root.Elements(XName.Get("Transactions")).Elements().AsEnumerable()
where transactions.Elements().Any
(
el =>
//String.IsNullOrEmpty(el.Value) &&
//elementsThatCannotBeEmpty.Contains(el.Name)
settingList.Any(
name => String.IsNullOrEmpty(el.Element(name.SettingValue).Value)
)
)
select new
{
CustomerName = transactions.Element(XName.Get("CustomerName")).Value,
ConfirmationNumber = transactions.Element(XName.Get("ConfirmationNumber")).Value
};
GridView.DataSource = transactionList;
GridView.DataBind();
XML File Example:
<OnlineBanking>
<Transactions>
<Txn>
<UserName>John Smith</UserName>
<CustomerStreet>123 Main</CustomerStreet>
<CustomerStreet2></CustomerStreet2>
<CustomerCity>New York</CustomerCity>
<CustomerState>NY</CustomerState>
<CustomerZip>12345</CustomerZip>
</Txn>
</Transactions>
</OnlineBanking>
Okay, first problem: if the element is missing, you'll get a NullReferenceException.
I'd suggest creating a List<string> of the elements which can't be null, to make the query simple. Then:
var requiredElements = settingList.Select(x => x.SettingValue).ToList();
var transactionList = root
.Elements("Transactions")
.Elements("Txn")
.Where(x => requiredElements
.Any(name => string.IsNullOrEmpty((string) x.Element(name)));
I think that should be okay, and slightly simpler than your original code... but to be honest, your original code looks like it should have worked anyway. What did it actually do? You haven't been very clear about the actual results versus the expected ones...
Something like this:
var transactionList =
root
.Elements(XName.Get("Transactions")) //Get <Transaction> elements
.Elements() //Get <Txn> elements
.Where(txn => txn.Elements().Any(e => e.Value == String.Empty)) //Filter <Txn> Elements if it have any element like this: <CustomerStreet2></CustomerStreet2>
.Select(x => new {
PropertyX = x.Element(XName.Get("UserName")),
PropertyY = x.Element(XName.Get("CustomerStreet")),
...
});
Works with:
<OnlineBanking>
<Transactions>
<Txn> <!-- This one matches! -->
<UserName>John Smith</UserName>
<CustomerStreet>123 Main</CustomerStreet>
<CustomerStreet2></CustomerStreet2>
<CustomerCity>New York</CustomerCity>
<CustomerState>NY</CustomerState>
<CustomerZip>12345</CustomerZip>
</Txn>
<Txn> <!-- This one doesn't match! -->
<UserName>John Smith</UserName>
<CustomerStreet>123 Main</CustomerStreet>
<CustomerStreet2>ASDASD</CustomerStreet2>
<CustomerCity>New York</CustomerCity>
<CustomerState>NY</CustomerState>
<CustomerZip>12345</CustomerZip>
</Txn>
</Transactions>
</OnlineBanking>