I have an XML file which includes some nodes and its children sometimes with the same name and attributes.
<?xml version="1.0"?>
<PurchaseOrder PurchaseOrderNumber="99503" OrderDate="1999-10-20">
<Address Type="Shipping">
<Name>Ellen Adams</Name>
<Street>123 Maple Street</Street>
<City>Mill Valley</City>
<State>CA</State>
<Zip>10999</Zip>
<Country>USA</Country>
</Address>
<Address Type="Billing">
<Name>Tai Yee</Name>
<Street>8 Oak Avenue</Street>
<City>Old Town</City>
<State>PA</State>
<Zip>95819</Zip>
<Country>USA</Country>
<Address Type="Buying">
<Name>Ellen Adams</Name>
</Address>
<Address Type="transporting">
<Name>Tai Yee</Name>
<Street>8 Oak Avenue</Street>
</Address>
</Address>
<DeliveryNotes>Please leave packages in shed by driveway.</DeliveryNotes>
<Items>
<Item PartNumber="872-AA">
<ProductName>Lawnmower</ProductName>
<Quantity>1</Quantity>
<USPrice>148.95</USPrice>
<Comment>Confirm this is electric</Comment>
</Item>
<Item PartNumber="926-AA">
<ProductName>Baby Monitor</ProductName>
<Quantity>2</Quantity>
<USPrice>39.98</USPrice>
<ShipDate>1999-05-21</ShipDate>
</Item>
</Items>
</PurchaseOrder>
I want to have just only the Address nodes with its attributes as output. The problem is that there are some nested nodes which have the Address node as a child. Getting access to the child Address is my problem. If again it has child with the name Address then I do not want to give me this node as I have done in the following code
public void find_node(string ID_node)
{
XElement root = XElement.Load("PurchaseOrder.xml");
IEnumerable<XElement> address =
from el in root.Elements("Address")
where (string)el.Attribute("Type") == "Buying"
select el;
foreach (XElement el in address)
{
var newElement = new XElement(
el.Name.LocalName,
el.Attributes(),
el.Elements().Where(o => o.Name.LocalName != "Address")
);
Console.WriteLine(newElement.ToString());
}
}
The desired output for this code would be
<Address Type="Buying">
<Name>Ellen Adams</Name>
</Address>
but it has empty output when I am running the above function.
May I ask you have could I get ride of empty output for this function?
If you mean to look for Address elements regardless of its location within the XML, as long as it matches the attribute filter, you can use Descendants("Address") instead :
IEnumerable<XElement> address =
from el in root.Descendants("Address")
where (string)el.Attribute("Type") == "Buying"
select el;
IEnumerable<XElement> address =
from el1 in root.Elements("Address")
from el2 in el1.Elements("Address")
where (string)el2.Attribute("Type") == "Buying"
select el2;
Related
Since I am new at Linq to XML, I practice using MS official document. I want the result Computer Keyboard from PurchaseOrderNumber="99504" and Item PartNumber="898-AZ" and ProductName. Can you give a guide for this?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace ParsingXML_220211
{
public class LINQtoXML
{
public LINQtoXML()
{
}
public void ParsingXML(string path)
{
XElement root = XElement.Load(path);
IEnumerable<XElement> purchaseOrders =
from po in root.Elements("PurchaseOrder")
where (from add in po.Elements("Address")
where (string)add.Attribute("Type") == "Billing" &&
(string)add.Element("City") == "Buffalo"
select add)
.Any()
select po;
foreach (XElement po in purchaseOrders)
{
var result = from item in po.Elements("Items")
where (string)item.Element("item").Attribute("PartNumber") == "898-AZ"
select item;
Console.WriteLine( result.Elements("ProductName").ToString());
}
}
}
}
Main
using System;
using System.Diagnostics;
using System.Xml;
namespace ParsingXML_220211
{
public class Program
{
//https://kibbomi.tistory.com/188
static void Main(string[] args)
{
//Linq to XML
LINQtoXML linQtoXml
= new LINQtoXML();
linQtoXml.ParsingXML("./../../../LinqToXML.xml");
}
}
}
LinqToXML.xml
<?xml version="1.0"?>
<PurchaseOrders>
<PurchaseOrder PurchaseOrderNumber="99503" OrderDate="1999-10-20">
<Address Type="Shipping">
<Name>Ellen Adams</Name>
<Street>123 Maple Street</Street>
<City>Mill Valley</City>
<State>CA</State>
<Zip>10999</Zip>
<Country>USA</Country>
</Address>
<Address Type="Billing">
<Name>Tai Yee</Name>
<Street>8 Oak Avenue</Street>
<City>Old Town</City>
<State>PA</State>
<Zip>95819</Zip>
<Country>USA</Country>
</Address>
<DeliveryNotes>Please leave packages in shed by driveway.</DeliveryNotes>
<Items>
<Item PartNumber="872-AA">
<ProductName>Lawnmower</ProductName>
<Quantity>1</Quantity>
<USPrice>148.95</USPrice>
<Comment>Confirm this is electric</Comment>
</Item>
<Item PartNumber="926-AA">
<ProductName>Baby Monitor</ProductName>
<Quantity>2</Quantity>
<USPrice>39.98</USPrice>
<ShipDate>1999-05-21</ShipDate>
</Item>
</Items>
</PurchaseOrder>
<PurchaseOrder PurchaseOrderNumber="99505" OrderDate="1999-10-22">
<Address Type="Shipping">
<Name>Cristian Osorio</Name>
<Street>456 Main Street</Street>
<City>Buffalo</City>
<State>NY</State>
<Zip>98112</Zip>
<Country>USA</Country>
</Address>
<Address Type="Billing">
<Name>Cristian Osorio</Name>
<Street>456 Main Street</Street>
<City>Buffalo</City>
<State>NY</State>
<Zip>98112</Zip>
<Country>USA</Country>
</Address>
<DeliveryNotes>Please notify me before shipping.</DeliveryNotes>
<Items>
<Item PartNumber="456-NM">
<ProductName>Power Supply</ProductName>
<Quantity>1</Quantity>
<USPrice>45.99</USPrice>
</Item>
</Items>
</PurchaseOrder>
<PurchaseOrder PurchaseOrderNumber="99504" OrderDate="1999-10-22">
<Address Type="Shipping">
<Name>Jessica Arnold</Name>
<Street>4055 Madison Ave</Street>
<City>Seattle</City>
<State>WA</State>
<Zip>98112</Zip>
<Country>USA</Country>
</Address>
<Address Type="Billing">
<Name>Jessica Arnold</Name>
<Street>4055 Madison Ave</Street>
<City>Buffalo</City>
<State>NY</State>
<Zip>98112</Zip>
<Country>USA</Country>
</Address>
<Items>
<Item PartNumber="898-AZ">
<ProductName>Computer Keyboard</ProductName>
<Quantity>1</Quantity>
<USPrice>29.99</USPrice>
</Item>
<Item PartNumber="898-AM">
<ProductName>Wireless Mouse</ProductName>
<Quantity>1</Quantity>
<USPrice>14.99</USPrice>
</Item>
</Items>
</PurchaseOrder>
</PurchaseOrders>
You have a typo here, it should be "Item"
where (string)item.Element("item").Attribute("PartNumber") == "898-AZ"
If you just want the word "Computer Keyboard", add a foreach
var result = from item in po.Elements("Items")
where (string)item.Element("Item").Attribute("PartNumber") == "898-AZ"
select item.Element("Item");
foreach (XElement item in result)
{
Console.WriteLine((string)item.Element("ProductName"));
}
I have an XML file with a list of parent nodes and childe nodes nested within the parent node, and I need to remove the child nodes when a specific criteria is been met.
Ex: Remove all contact nodes where the id = 1. How can I achieve this using linq and xml. This is my XML structure
<Events>
<Event>
<id>1</id>
<title>AA</title>
<start>2019-12-01T14:13:58.863</start>
<end>2019-12-01T15:13:58.787</end>
<contacts>
<contact>
<id>1</id>
<name>ABC</name>
</contact>
<contact>
<id>2</id>
<name>ABCD</name>
</contact>
<contact>
<id>3</id>
<name>ABCDE</name>
</contact>
</contacts>
</Event>
<Event>
<id>2</id>
<title>BB</title>
<start>2019-12-01T14:13:58.863</start>
<end>2019-12-01T15:13:58.787</end>
<contacts>
<contact>
<id>1</id>
<name>ABC</name>
</contact>
<contact>
<id>2</id>
<name>ABCD</name>
</contact>
<contact>
<id>3</id>
<name>ABCDE</name>
</contact>
</contacts>
</Event>
</Events>
You can get the XML nodes using this query
var query = xmlDoc.Descendants("contact").Where(e => e.Element("id").Value.Equals(id)).ToList();
And then run
query.Remove()
to remove the elements that were returned.
As Jon Skeet pointed out there is no need to do anything esoteric. Here is a complete example how to do it. Pure LINQ to XML.
c#, LINQ to XML
void Main()
{
const string inputXML = #"e:\Temp\MikeOconner.xml";
const string outputXML = #"e:\Temp\MikeOconner_output.xml";
XDocument xml = XDocument.Load(inputXML);
xml.Root.DescendantsAndSelf("contact")
.Where(r => (string)r.Element("id").Value == "1")
.Remove();
xml.Save(outputXML);
}
I have the following XML file and want to find an element with a specific Attribute. What I wan tot find is looking for an attribute with "Type" == "Billing".
<?xml version="1.0"?>
<PurchaseOrder PurchaseOrderNumber="99503" OrderDate="1999-10-20">
<Address Type="Shipping">
<Name>Ellen Adams</Name>
<Street>123 Maple Street</Street>
<City>Mill Valley</City>
<State>CA</State>
<Zip>10999</Zip>
<Country>USA</Country>
</Address>
<Address Type="Billing">
<Name>Tai Yee</Name>
<Street>8 Oak Avenue</Street>
<City>Old Town</City>
<State>PA</State>
<Zip>95819</Zip>
<Country>USA</Country>
<Address Type="Shipping">
<Name>Ellen Adams</Name>
</Address>
<Address Type="transporting">
<Name>Tai Yee</Name>
<Street>8 Oak Avenue</Street>
</Address>
</Address>
</PurchaseOrder>
The problem is that here second Address node has some other inner nodes that would not be related to what I really wan to have. I mean attributes with the Shipping and transporting should not be in the out put. If I think of have only the following output what code would be desired?
<Address Type="Billing">
<Name>Tai Yee</Name>
<Street>8 Oak Avenue</Street>
<City>Old Town</City>
<State>PA</State>
<Zip>95819</Zip>
<Country>USA</Country>
</Address>
and the code which I have is:
XElement root = XElement.Load("PurchaseOrder.xml");
IEnumerable<XElement> address =
from el in root.Elements("Address")
where (string)el.Attribute("Type") == "Billing"
select el;
foreach (XElement el in address)
Console.WriteLine(el);
Everything between opening and closing tags are part of an element. To get the desired output, you need to either 1. reconstruct the element to include only the needed information :
foreach (XElement el in address)
{
var newElement = new XElement(
el.Name.LocalName,
el.Attributes(),
el.Elements().Where(o => o.Name.LocalName != "Address")
);
Console.WriteLine(newElement.ToString());
}
or 2. remove all unnecessary information i.e Address elements :
foreach (XElement el in address)
{
el.Elements("Address").Remove();
Console.WriteLine(el.ToString());
}
Here is how I would do it
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication87
{
namespace Linq
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
List<string> acceptableElements = new List<string>(){"Name", "Street", "City", "State", "Zip", "Country"};
XDocument doc = XDocument.Load(FILENAME);
List<XElement> addresses = doc.Descendants("Address").ToList();
XElement billing = addresses.Where(x => x.Attribute("Type").Value == "Billing").FirstOrDefault();
addresses.Remove();
XElement po = (XElement)doc.FirstNode;
po.Add(billing);
}
}
}
}
I used the follwoing code to search the number of elements and search the search is succeeded only if there is no default path:
The code to search:
XElement root = XElement.Load(#"c:\b.txt", LoadOptions.PreserveWhitespace);
IEnumerable<XElement> address =
from el in root.Elements("Address")
select el;
int c = address.Count();
And the value for c is 2 with the following data:
<?xml version="1.0" encoding="UTF-8"?>
<presence xmlns:a="urn:ietf:params:xml:ns:pidf"
xmlns:dm="urn:ietf:params:xml:ns:pidf:data-model"
xmlns:oma="urn:xml:prs:pidf:oma-pres"
entity="sip:john#police.city.gov">
<Address Type="Shipping">
<Name>Ellen Adams</Name>
<Street>123 Maple Street</Street>
<City>Mill Valley</City>
<State>CA</State>
<Zip>10999</Zip>
<Country>USA</Country>
</Address>
<Address Type="Billing">
<Name>Tai Yee</Name>
<Street>8 Oak Avenue</Street>
<City>Old Town</City>
<State>PA</State>
<Zip>95819</Zip>
<Country>USA</Country>
</Address>
</presence>
But if I change the XML by exchanging the second line to be (xmlns instead of xmlns:a):
<presence xmlns="urn:ietf:params:xml:ns:pidf"
I got value 0 which is incorrect.
Any suggestion?
Thanks
xmlns="urn:ietf:params:xml:ns:pidf" means, that you set default namespace for all elements in xml, that don't have any namespace specified.
As result, you should also add namespace declaration to your LINQ to XML query, like so:
XElement root = XElement.Load(#"c:\b.txt", LoadOptions.PreserveWhitespace);
XNamespace xmlns = "urn:ietf:params:xml:ns:pidf";
IEnumerable<XElement> address = root.Elements(xmlns + "Address");
Console.WriteLine(address.Count()); //prints 2
or you can use namespace agnostic approach, which will work regardless of what default namespace is specified:
var address = root.Elements()
.Where(node => node.Name.LocalName == "Address");
//address will contain the same nodes, as in previous example
Also note, that extension method syntax is much cleaner in such case.
By setting xmlns="urn:ietf:params:xml:ns:pidf" you've set the default namespace for all elements. Therefore, the "Address" element no longer exists. It's called "urn:ietf:params:xml:ns:pidf:Address" now.
What you need to do is declare an XNamespace and add that to the element name:
XNamespace defaultNamespace = "urn:ietf:params:xml:ns:pidf";
XElement root = XElement.Load(#"c:\b.txt", LoadOptions.PreserveWhitespace);
IEnumerable<XElement> address =
from el in root.Elements(defaultNamespace + "Address")
select el;
int c = address.Count();
<InventoryList>
<Product xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Id>1</Id>
<Name>Pizza Ristorante Hawaii</Name>
<Price>2.99</Price>
<VariableWeightPrice>€ 8,42 / kg</VariableWeightPrice>
<Brand>Dr.Oetker</Brand>
<PackageInfo>355 GR</PackageInfo>
<categoryString />
<PictureSmallFilename>1small.jpg</PictureSmallFilename>
<InformationTakenFrom>Jumbo</InformationTakenFrom>
<MarketItemUrl></MarketItemUrl>
<BarCode>4001724819608</BarCode>
<IsBlackListed>false</IsBlackListed>
<ItemLists>
<Item>
<ListName>in</ListName>
<Quantity>1</Quantity>
<QuantityWeight>0</QuantityWeight>
</Item>
<Item>
<ListName>out</ListName>
<Quantity>2</Quantity>
<QuantityWeight>0</QuantityWeight>
</Item>
</ItemLists>
</Product>
<Product xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Id>2</Id>
<Name>Produto 2</Name>
<Price>2.99</Price>
<VariableWeightPrice>€ 5,55 / kg</VariableWeightPrice>
<Brand>Dr.Oetker</Brand>
<PackageInfo>355 GR</PackageInfo>
<categoryString />
<PictureSmallFilename>1small.jpg</PictureSmallFilename>
<InformationTakenFrom>Jumbo</InformationTakenFrom>
<MarketItemUrl></MarketItemUrl>
<BarCode>4001724819608</BarCode>
<IsBlackListed>false</IsBlackListed>
<ItemLists>
<Item>
<ListName>out</ListName>
<Quantity>1</Quantity>
<QuantityWeight>0</QuantityWeight>
</Item>
</ItemLists>
</Product>
</InventoryList>
thanks in advance for your help.
I have this xml database
i want to return all products that have the ListName = "out", but this query i´m trying it´s only returning the second product, and i need it to return the first and the second product.
var _queryItems = from c in xml.Descendants("Product")
where
c.Element("ItemLists").Element("Item").Element("ListName").Value == "out"
select c;
thanks :)
Right now you just check the first Item element, instead you want to check if any Item's ListName matches "out":
var _queryItems = from c in xml.Descendants("Product")
where c.Element("ItemLists")
.Elements("Item").Any( x=> x.Element("ListName").Value == "out")
select c;