I am fairly new to LINQ and XML and am trying to work with an existing large 500k line XML file which has the same structure as the XML below. I have figured out how to test for multiple null XElements but am totally stuck on how to search for multiple Identical XElements.
How do I get LINQ to return just contacts that work for google?
Thank you all in advanced.
void Main()
{
XDocument AddressBook = CreateAddressBookXML();
var query =
from contact in AddressBook.Descendants("Contact")
let companyelement = contact.Element("Company")
where companyelement != null
let companyname = companyelement.Descendants("CompanyName")
where companyname != null && companyname == "Google"
select contact;
Console.Write(query);
}
public XDocument CreateAddressBookXML() {
XDocument result =
new XDocument(
new XComment("My phone book"),
new XElement("phoneBook",
new XComment("My friends"),
new XElement("Contact",
new XAttribute("name", "Ralph"),
new XElement("homephone", "555-234-4567"),
new XElement("cellphone", "555-345-75656"),
new XElement("Company",
new XElement("CompanyName","Ralphs Web Design"),
new XElement("CompanyName","Google")
)
),
new XElement("Contact",
new XAttribute("name", "Dave"),
new XElement("homephone", "555-756-9454"),
new XElement("cellphone", "555-762-1546"),
new XElement("Company",
new XElement("CompanyName","Google")
)
),
new XComment("My family"),
new XElement("Contact",
new XAttribute("name", "Julia"),
new XElement("homephone", "555-578-1053"),
new XElement("cellphone", "")
),
new XComment("My team"),
new XElement("Contact",
new XAttribute("name", "Robert"),
new XElement("homephone", "555-565-1653"),
new XElement("cellphone", "555-456-2567"),
new XElement("Company",
new XElement("CompanyName","Yahoo")
)
)
)
);
return result;
}
var query = from contacts in CreateAddressBookXML().Root.Descendants("Contact")
where contacts.Element("Company") != null &&
contacts.Element("Company").Elements("CompanyName").
FirstOrDefault(c => c.Value == "Google") != null
select contacts;
I generally prefer to mix in a bit of XPath to write these queries, it's far more compact than the LINQ equivalent.
var query =
from contact in doc.XPathSelectElements("/phoneBook/Contact")
where contact.XPathSelectElements("Company/CompanyName[.='Google']").Any()
select contact;
Otherwise, using LINQ:
var query =
from contact in doc.Elements("phoneBook").Elements("Contact")
where contact.Elements("Company").Elements("CompanyName")
.Any(c => (string)c == "Google")
select contact;
Related
I want to create a table that list the count of each file type. I've created a query to get that data. When I create the XDocument, how do I execute the query and create rows in the table with data from query?
var query = listFiles.GroupBy(f => Path.GetExtension(f).ToLower())
.Select(g => new
{
Extension = g.Key,
Count = g.Count(),
});
var doc = new XDocument(
new XElement("html",
new XElement("body",
new XElement("table", new XAttribute("border", 2),
foreach (var f in query)
{
new XElement("tr",
new XElement("td", f.Extension),
new XElement("td", f.Count));
}))));
Try following :
var doc = new XDocument(
new XElement("html",
new XElement("body",
new XElement("table", new object[] {
new XAttribute("border", 2),
query.Select(f =>
new XElement("tr",
new XElement("td", f.Extension),
new XElement("td", f.Count)))
}))));
This is my code:
var xml = new XElement("test", new[] {
new XElement("group", new[] {
new XElement("date", dateNow.ToString("dd/MM/yyyy HH:mm:ss"))
}),
new XElement("users", new[] {
foreach(var item in in PlaceHolderCustom.Controls)
{
new XElement("nome", ((TextBox)item.FindControl("myTextBox")).Text)
}
})
});
I'd like to set in the xml some fixed fields (within the element "group") and some that would iterate across a placeholder. But the syntax seems to be wrong when I try to add a new "iterating" list.
Where am I wrong?
Use linq .Select to perform the foreach. Also when you create the array the new [] {} syntax is valid only for new string[]. In your case use:
new XElement[] {}
Or because the method gets a params object[] you can just give each new XElement independently without wrapping with an array
So showing both ways of passing the collection of XElements:
var xml = new XElement("test",
new XElement("group", new XElement[] {
new XElement("date", dateNow.ToString("dd/MM/yyyy HH:mm:ss"))
}),
new XElement("users", PlaceHolderCustom.Control.Select(item =>
new XElement("nome", ((TextBox)item.FindControl("myTextBox")).Text)).ToArray())
);
I've got this XML:
<?xml version="1.0" encoding="utf-8"?>
<JMF SenderID="InkZone-Controller" Version="1.2">
<Command ID="cmd.00695" Type="Resource">
<ResourceCmdParams ResourceName="InkZoneProfile" JobID="K_41">
<InkZoneProfile ID="r0013" Class="Parameter" Locked="false" Status="Available" PartIDKeys="SignatureName SheetName Side Separation" DescriptiveName="Schieberwerte von DI" ZoneWidth="32">
<InkZoneProfile SignatureName="SIG1">
<InkZoneProfile Locked="False" SheetName="S1">
<InkZoneProfile Side="Front">
<ColorPool Class="Parameter" DescriptiveName="Colors for the job" Status="Available">
<InkZoneProfile Separation="PANTONE 647 C" ZoneSettingsX="0 0,003 " />
</ColorPool>
</InkZoneProfile>
</InkZoneProfile>
</InkZoneProfile>
</InkZoneProfile>
</ResourceCmdParams>
</Command>
</JMF>
I'm trying to add a node after a specific node() , using XElement and Linq. But my LINQ query always returns me null.
Tried this:
XElement InkZonePath = XmlDoc.Element("JMF").Elements("InkZoneProfile").Where(z => z.Element("InkZoneProfile").Attribute("Side").Value == "Front").SingleOrDefault();
And this:
XmlDoc.Element("JMF")
.Elements("InkZoneProfile").Where(InkZoneProfile => InkZoneProfile.Attribute("Side")
.Value == "Front").FirstOrDefault().AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", x.colorname),
new XAttribute("ZoneSettingsX", x.colorvalues)));
I've built this queries following those examples:
Select XElement where child element has a value
Insert XElements after a specific node
LINQ-to-XML XElement query NULL
But it didn't worked as expected. What is wrong with the LINQ Query ? From what i've read it should work (logically reading the expression i can understand it).
Thanks
EDIT-1: Entire writexml Method
public void writexml(xmldatalist XMLList, variables GlobalVars)
{
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
IndentChars = "\t",
NewLineChars = Environment.NewLine,
NewLineHandling = NewLineHandling.Replace,
Encoding = new UTF8Encoding(false)
};
string DesktopFolder = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
string FileExtension = ".xml";
string PathString = Path.Combine(DesktopFolder, "XML");
System.IO.Directory.CreateDirectory(PathString);
foreach (List<xmldata> i in XMLList.XMLArrayList)
{
int m = 0;
foreach (var x in i)
{
string XMLFilename = System.IO.Path.GetFileNameWithoutExtension(x.xml_filename);
GlobalVars.FullPath = Path.Combine(PathString, XMLFilename + FileExtension);
if (!File.Exists(GlobalVars.FullPath))
{
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("JMF",
new XAttribute("SenderID", "InkZone-Controller"),
new XAttribute("Version", "1.2"),
new XElement("Command",
new XAttribute("ID", "cmd.00695"),
new XAttribute("Type", "Resource"),
new XElement("ResourceCmdParams",
new XAttribute("ResourceName", "InkZoneProfile"),
new XAttribute("JobID", "K_41"),
new XElement("InkZoneProfile",
new XAttribute("ID", "r0013"),
new XAttribute("Class", "Parameter"),
new XAttribute("Locked", "False"),
new XAttribute("Status", "Available"),
new XAttribute("PartIDKeys", "SignatureName SheetName Side Separation"),
new XAttribute("DescriptiveName", "Schieberwerte von DI"),
new XAttribute("ZoneWidth", "32"),
new XElement("InkZoneProfile",
new XAttribute("SignatureName", "SIG1"),
new XElement("InkZoneProfile",
new XAttribute("Locked", "false"),
new XAttribute("SheetName", "S1"),
new XElement("InkZoneProfile",
new XAttribute("Side", "Front"),
new XElement("ColorPoolClass",
new XAttribute("Class", "Parameter"),
new XAttribute("DescriptiveName", "Colors for the job"),
new XAttribute("Status", "Available")
)))))))));
doc.Save(GlobalVars.FullPath);
XDocument XmlDoc = new XDocument();
XmlDoc = XDocument.Load(GlobalVars.FullPath);
XElement InkZonePath = XmlDoc.Root.Descendants("InkZoneProfile").Where(z => (string)z.Attribute("Side") == "Front").SingleOrDefault();
if (InkZonePath != null)
{
InkZonePath.AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", x.colorname),
new XAttribute("ZoneSettingsX", x.colorvalues)));
}
XmlDoc.Save(GlobalVars.FullPath);
}//Closing !FileExists
}//Closing inner foreach
}//Closing outer foreach
}//Closing writexml method
The problem with your current code is here : Element("JMF").Elements("InkZoneProfile") Since InkZoneProfile is not a direct child of JMF it will not return anything. Use Descendants instead.
Check difference between Elements & Descendants.
This should give you correct result:-
XElement InkZonePath = xdoc.Element("JMF").Descendants("InkZoneProfile")
.SingleOrDefault(z => (string)z.Attribute("Side") == "Front")
After this you can add whatever node you want to add using AddAfterSelf. Also note I have used SingleOrDefault here, but you may get exception if you have multiple matching nodes with this, In that case consider using FirstOrDefault.
Update:
To add new node:-
if (InkZonePath != null)
{
InkZonePath.AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", "Test"),
new XAttribute("ZoneSettingsX", "Test2")));
}
//Save XDocument
xdoc.Save(#"C:\Foo.xml");
You need to use Descendants method instead Elements:
XElement InkZonePath = XmlDoc.Root.Descendants("InkZoneProfile").Where(z => (string)z.Attribute("Side") == "Front").SingleOrDefault();
if(InkZonePath !=null)
InkZonePath.AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", x.colorname),
new XAttribute("ZoneSettingsX", x.colorvalues)));
you can use Descendants instead.
var node = XmlDoc.Descendants("InkZoneProfile").Where(x=> x.Attribute("Side") !=null && x.Attribute("Side").Value == "Front").FirstorDefault();
I would like to generate an XDocument from my table (the structure is different) but I keep getting the following error :
LINQ to Entities does not recognize the method 'System.String ToString()' method.
I know this is caused by the Birthdate and I would need to use SqlFunctions.StringConvert but I'm working with Framework 4.0
Any ideas on how to solve that?
// Newsletter subscriptions
var news = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
new XElement("USER",
from n in _dc.Newsletter_Datas
where n.Timestamp >= startDate
select
new XElement("ROW",
new XElement("UTI", n.ID),
new XElement("FIRSTNAME", n.FirstName),
new XElement("FAMILYNAME", n.LastName),
new XElement("GENDER", n.Gender),
new XElement("BIRTHDATE", n.BirthDate != null ? n.BirthDate.Value.ToString("yyyy-MM-dd") : "2003-01-01"),
new XElement("LANGUAGE", n.Language),
new XElement("EMAIL", n.Email),
new XElement("STREETNAME", n.StreetName),
new XElement("HOUSENR", n.HouseNr),
new XElement("BOXNR", n.BoxNr),
new XElement("ZIPCODE", n.Zipcode),
new XElement("CITY", n.City),
new XElement("COUNTRY", n.Country),
new XElement("TS", n.Timestamp.ToString("yyyy-MM-dd hh:mm:ss")),
new XElement("MESSAGE_ID", "SCNEWS02"),
new XElement("OPT_INS",
new XElement("OPT_IN",
new XElement("OPT_IN_CBP", "01000000000"),
new XElement("OPT_IN_INSERT_TS", n.Timestamp.ToString("yyyy-MM-dd hh:mm:ss")),
new XElement("OPT_IN_METHOD", "1"),
new XElement("OPT_IN_TYPE", n.OptIn),
new XElement("OPT_IN_CHANNEL", "2"))))));
Thank you very much
Break your query into separate parts, the first part is the query itself. That will be handled by the entity framework. Then cast that query to an IEnumerable<NewsletterData> to finish it off using LINQ to objects. Entity Framework can't do more than a simple projection (which you are clearly not doing).
var query =
from n in _dc.Newsletter_Datas
where n.Timestamp >= startDate
select n;
var news = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
new XElement("USER",
from n in query.AsEnumerable() // LINQ to objects
select new XElement("ROW",
new XElement("UTI", n.ID),
new XElement("FIRSTNAME", n.FirstName),
new XElement("FAMILYNAME", n.LastName),
new XElement("GENDER", n.Gender),
new XElement("BIRTHDATE", n.BirthDate != null ? n.BirthDate.Value.ToString("yyyy-MM-dd") : "2003-01-01"),
new XElement("LANGUAGE", n.Language),
new XElement("EMAIL", n.Email),
new XElement("STREETNAME", n.StreetName),
new XElement("HOUSENR", n.HouseNr),
new XElement("BOXNR", n.BoxNr),
new XElement("ZIPCODE", n.Zipcode),
new XElement("CITY", n.City),
new XElement("COUNTRY", n.Country),
new XElement("TS", n.Timestamp.ToString("yyyy-MM-dd hh:mm:ss")),
new XElement("MESSAGE_ID", "SCNEWS02"),
new XElement("OPT_INS",
new XElement("OPT_IN",
new XElement("OPT_IN_CBP", "01000000000"),
new XElement("OPT_IN_INSERT_TS", n.Timestamp.ToString("yyyy-MM-dd hh:mm:ss")),
new XElement("OPT_IN_METHOD", "1"),
new XElement("OPT_IN_TYPE", n.OptIn),
new XElement("OPT_IN_CHANNEL", "2")
)
)
)
)
);
what is the best way of reading xml file using linq and the below code you will see that, I have three different loops and I feel like its not elegant or do I have options to retrofit the below code?
public static void readXMLOutput(Stream stream)
{
XDocument xml = new XDocument();
xml = LoadFromStream(stream);
var header = from p in xml.Elements("App").Elements("Application")
select p;
foreach (var record in header)
{
string noym = record.Element("nomy").Value;
string Description = record.Element("Description").Value;
string Name = record.Element("Name").Value;
string Code = record.Element("Code").Value;
}
var appRoles = from q in xml.Elements("App").Elements("Application").Elements("AppRoles").Elements("Role")
select q;
foreach (var record1 in appRoles)
{
string Name = record1.Element("Name").Value;
string modifiedName = record1.Element("ModifiedName").Value;
}
var memeber = from r in xml.Elements("App").Elements("Application").Elements("AppRoles").Elements("Role").Elements("Members")
select r;
foreach (var record2 in memeber)
{
string ExpirationDate = record2.Element("ExpirationDate").Value;
string FullName = record2.Element("FullName").Value;
}
}
UPDATED:
foreach (var record in headers)
{
..............
string Name1 = record.Attribute("Name").Value;
string UnmodifiedName = record.Attribute("UnmodifiedName").Value;
string ExpirationDate = record.Attribute("ExpirationDate").Value;
string FullName = record.Attribute("FullName").Value;
...............
}
Is that your actual code ? All those string variables you are assigning in the foreach loops only have a scope of one iteration of the loop. They are created and destroyed each time.
This may not work precisely in your case depending on the xml structure. Play around with it. Try it using LinqPad
var applications = from p in xml.Descendants("Application")
select new { Nomy = p.Element("nomy").Value
, Description = p.Element("Description").Value
, Name = p.Element("Name").Value
, Code = p.Element("Code").Value
};
var appRoles = from r in xml.Descendants("Role")
select new { Name = r.Element("Name").Value
, ModifiedName = r.Element("ModifiedName").Value
};
This answer is a hierarchical query.
var headers =
from header in xml.Elements("App").Elements("Application")
select new XElement("Header",
new XAttribute("noym", header.Element("nomy").Value),
new XAttribute("Description", header.Element("Description").Value),
new XAttribute("Name", header.Element("Name").Value),
new XAttribute("Code", header.Element("Code").Value),
from role in header.Elements("AppRoles").Elements("Role")
select new XElement("Role",
new XAttribute("Name", role.Element("Name").Value),
new XAttribute("ModifiedName", role.Element("ModifiedName").Value),
from member in role.Elements("Members")
select new XElement("Member",
new XAttribute("ExpirationDate", member.Element("ExpirationDate").Value),
new XAttribute("FullName", member.Element("FullName").Value)
)
)
);