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>
Related
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 an XML file that looks like this -
<SST_SignageCompConfig>
<Items>
<Item>
<Index>0</Index>
<Type>1</Type>
<Duration>7</Duration>
<Name>Branding-Colours-for-business.jpg</Name>
</Item>
<Item>
<Index>1</Index>
<Type>1</Type>
<Duration>7</Duration>
<Name>Flower of Life Meditation - Copy.png</Name>
</Item>
</Items>
</SST_SignageCompConfig>
I need to count how many Item Elements there are within the Items Element.
ie how many images there are.
I'm using XDocument, so my XML file is loaded like this -
string configurationPath = System.IO.Path.Combine("C:\\SST Software\\DSS\\Compilations\\" + compName + #"\\Comp.cfg");
XDocument filedoc = XDocument.Load(configurationPath);
I've tried numerous variations of the following, with all returning a null object reference exception
foreach (var item in filedoc.Element("SST_SignageCompConfig").Element("Items").Element("Item").Nodes())
{
string name = filedoc.Element("SST_SignageCompConfig").Element("Items").Element("Item").Attribute("Name").ToString();
files.Append(name + "|");
}
I've found countless examples of how to count how many different child elements are within an element, but I need to know how many instances of the same element exist.
Can anyone point me in the right direction?
You can select all names like so:
var names = from item in filedoc.Descendants("Item")
select (string)item.Element("Name");
Or without the query syntax:
var names = filedoc.Descendants("Item").Elements("Name").Select(e => e.Value);
You can get only unique names by:
var uniqueNames = names.Distinct();
You're on the right track. Try finding out exactly which invocation is giving you the NullReferenceException. My guess is that it's the attempt to find:
.Element("SST_SignageCompConfig")
Which is your root. Try the following instead:
// note the difference between .Element and .Elements
var count = filedoc.Root.Element("Items").Elements("Item").Count();
You could also use XPath to help you nail down the navigation within your XDocument:
// returns the current top level element
var element = filedoc.Root.XPathSelectElement(".");
// If the returned element is "SST_SignageCompConfig", then:
var nextElement = filedoc.Root.XPathSelectElement("./Items")
// If the "." element is *not* "SST_SignageCompConfig", then try and locate where in your XML document that node is.
// You can navigate up with .Parent and down with .Element(s)
And so on.
How about:
var nav = fileDoc.CreateNavigator();
XPathNodeIterator navShape = nav.Select("/SST_SignageCompConfig/Items");
navShape.MoveNext()
var count = navShape.Count;
If your xml has only one Items element, this should do the trick:
filedoc.Descendants("Item")
.GroupBy(e => e.Element("Name")!=null? e.Element("Name").Value:String.Empty)
.Select(g => new
{
Name = g.Key,
Count = g.Count()
});
Because "Name" is an element and not an attribute of your xml structure.
can you try replacing this?
string name = filedoc.Element("SST_SignageCompConfig").Element("Items").Element("Item").Element("Name").ToString();
i just stuck in an issue when ever i parse my XML like this using Xdocument :
XDocument xmldoc = XDocument.Load(datafromxml);
var data = from query in xmldoc.Descendants("Chapter")
select new MyEntityclass
{
Sampledata = (string)query.Element("SubChapter")
};
i got only one tag inner value from this. i.e from the first tag value only. remaining are skipped.
My XML is like :
<Chapter>
<SubChapter ChapterID="1"><![CDATA["Some data here 1"]]></SubChapter>
<SubChapter ChapterID="2"><![CDATA["Some data here 2"]]></SubChapter>
<SubChapter ChapterID="3"><![CDATA["Some data here 3"]]></SubChapter>
</Chapter>
when i checked in a debug i just got the value of "chapterid : 1". please help me to sort out this. thanks
Your query now only retreives (iterates) the outer node.
You need something like (untested)
var data = from query in xmldoc.Descendants("Chapter")
from chapter in query.Elements("SubChapter") // note the 's'
select new MyEntityclass
{
Sampledata = (string)chapter
};
var data = xmlDoc.Root
.Elements("SubChapter")
.Select(x => new MyEntityclass { Sampledata = (string)x });
I have this XML file:
<MyXml>
<MandatoryElement1>value</MandatoryElement1>
<MandatoryElement2>value</MandatoryElement2>
<MandatoryElement3>value</MandatoryElement3>
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
<MyXml>
All 3 elements that are called 'MandatoryElementX' will always appear in the file. The elements called 'CustomElementX' are unknown. These can be added or removed freely by a user and have any name.
What I need is to fetch all the elements that are not MandatoryElements. So for the file above I would want this result:
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
I don't know what the names of the custom elements may be, only the names of the 3 MandatoryElements, so the query needs to somehow exclude these 3.
Edit:
Even though this was answered, I want to clarify the question. Here is an actual file:
<Partner>
<!--Mandatory elements-->
<Name>ALU FAT</Name>
<InterfaceName>Account Lookup</InterfaceName>
<RequestFolder>C:\Documents and Settings\user1\Desktop\Requests\ALURequests</RequestFolder>
<ResponseFolder>C:\Documents and Settings\user1\Desktop\Responses</ResponseFolder>
<ArchiveMessages>Yes</ArchiveMessages>
<ArchiveFolder>C:\Documents and Settings\user1\Desktop\Archive</ArchiveFolder>
<Priority>1</Priority>
<!--Custom elements - these can be anything-->
<Currency>EUR</Currency>
<AccountingSystem>HHGKOL</AccountingSystem>
</Partner>
The result here would be:
<Currency>EUR</Currency>
<AccountingSystem>HHGKOL</AccountingSystem>
You can define a list of mandatory names and use LINQ to XML to filter:
var mandatoryElements = new List<string>() {
"MandatoryElement1",
"MandatoryElement2",
"MandatoryElement3"
};
var result = xDoc.Root.Descendants()
.Where(x => !mandatoryElements.Contains(x.Name.LocalName));
Do you have created this xml or do you get it by another person/application?
If it's yours I would advise you not to number it. You can do something like
<MyXml>
<MandatoryElement id="1">value<\MandatoryElement>
<MandatoryElement id="2">value<\MandatoryElement>
<MandatoryElement id="3">value<\MandatoryElement>
<CustomElement id="1">value<\CustomElement>
<CustomElement id="2">value<\CustomElement>
<MyXml>
In the LINQ-Statement you don't need the List then.
Your question shows improperly formatted XML but I am assuming that is a typo and the real Xml can be loaded into the XDocument class.
Try this...
string xml = #"<MyXml>
<MandatoryElement1>value</MandatoryElement1>
<MandatoryElement2>value</MandatoryElement2>
<MandatoryElement3>value</MandatoryElement3>
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
</MyXml> ";
System.Xml.Linq.XDocument xDoc = XDocument.Parse(xml);
var result = xDoc.Root.Descendants()
.Where(x => !x.Name.LocalName.StartsWith("MandatoryElement"));
lets say TestXMLFile.xml will contain your xml,
XElement doc2 = XElement.Load(Server.MapPath("TestXMLFile.xml"));
List<XElement> _list = doc2.Elements().ToList();
List<XElement> _list2 = new List<XElement>();
foreach (XElement x in _list)
{
if (!x.Name.LocalName.StartsWith("Mandatory"))
{
_list2.Add(x);
}
}
foreach (XElement y in _list2)
{
_list.Remove(y);
}
Here's my XML File:
<Applications>
<Application Name="Abc">
<Section Name="xyz">
<Template Name="hello">
...
....
</Template>
</Section>
</Application>
<Application Name="Abc1">
<Section Name="xyz1">
<Template Name="hello">
...
....
</Template>
</Section>
</Application>
What I need to do is get the Template XElement from the given structure based upon the Name attribute of Template tag. The problem is there can be multiple template tags with same attribute Name. The Distinguishing factor is Application Name attribute value and section attribute value.
Currently I'm able to get the XElement by first getting Application Element based upon it's attribute, then Section based upon it's attribute and then finally template based upon it' name.
I wanted to know if there is a way to get it in one go.
I would use the fact that you can call Elements or an existing sequence, so:
var template = doc.Descendants("Application")
.Where(x => (string) x.Attribute("Name") == applicationName)
.Elements("Section")
.Where(x => (string) x.Attribute("Name") == sectionName)
.Elements("Template")
.Where(x => (string) x.Attribute("Name") == templateName)
.FirstOrDefault();
You might even want to add an extension method somewhere:
public static IEnumerable<XElement> WithName(this IEnumerable<XElement> elements,
string name)
{
this elements.Where(x => (string) x.Attribute("Name") == name);
}
Then you can rewrite the query as:
var template = doc.Descendants("Application").WithName(applicationName)
.Elements("Section").WithName(sectionName)
.Elements("Template").WithName(templateName)
.FirstOrDefault();
... which I think you'll agree is quite readable :)
Note that the use of casting XAttribute to string instead of using the Value property means that any elements without the Name attribute are just effectively ignored rather than causing a NullReferenceException.
The following code should do the trick:
var template = doc.Descendants("Template")
.Where(x => x.Attribute("Name").Value == "hello"
&& x.Parent.Attribute("Name").Value == "xyz1"
&& x.Parent.Parent.Attribute("Name").Value == "Abc1");
Please note that this code throws exceptions if the XML doesn't conform to the specification. Specifically, there will be a NullReferenceException if any of the tags in question don't contain an attribute named "Name". Or if the Template tag doesn't have two levels of parents.
XDocument doc = XDocument.Load("Path of xml");
var selection =
doc.Descendants("Section").Select(item => item).Where(
item => item.Attribute("Name").Value.ToString().Equals("Section Name Value")).ToList();
if(null != selection)
{
var template =
selection.Descendants("Template").Select(item => item).Where(
item => item.Attribute("Name").Value.ToString().Equals("Template name value"));
}
XPath should help you. Use the Extensions.XPathSelectElement Method (XNode, String) :
XDocument xdoc = XDocument.Load("yourfile.xml");
string xPathQuery = string.Format(
"/Applications/Application[#Name='{0}']/Section[#Name='{1}']/Template[#Name='{2}']",
"MyApplication",
"MySection",
"MyTemplate"
);
XElement template = xdoc.Root.XPathSelectElement(xPathQuery);