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);
}
}
}
}
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 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;
I have the following XML...
<?xml version="1.0" encoding="utf-8"?>
<Employees>
<Employee>
<EmpId>1</EmpId>
<Name>Sam</Name>
<Sex>Male</Sex>
<Phone Type="Home">423-555-0124</Phone>
<Phone Type="Work">424-555-0545</Phone>
<Address>
<Street>7A Cox Street</Street>
<City>Acampo</City>
<State>CA</State>
<Zip>95220</Zip>
<Country>USA</Country>
</Address>
<Employee>
<EmpId>5</EmpId>
<Name>Kenneth Lowrey</Name>
<Sex>Male</Sex>
<Phone Type="Home">555-555-3477</Phone>
<Phone Type="Work">444-123-2557</Phone>
<Address>
<Street>6026 Amberwoods Drive</Street>
<City>Boca Raton</City>
<State>FL</State>
<Zip>33433</Zip>
<Country>USA</Country>
</Address>
</Employee>
</Employee>
</Employees>
I have the following Console Application code...
using System;
using System.Xml.Linq;
using System.Linq;
class Program
{
public static void Main(String[] args)
{
String strPath = #"C:\XMLExample\Employees.xml";
XElement xEle = XElement.Load(strPath);
var empquery = from e in xEle.Descendants("Employee")
select new
{
name = e.Element("Name").Value,
homephone = (string)e.Element("Phone").Attribute("Type").Value=="Home" ? e.Element("Phone").Value : "",
workphone = (string)e.Element("Phone").Attribute("Type").Value=="Work" ? e.Element("Phone").Value : "",
};
foreach (var e in empquery)
{
Console.WriteLine("{0}'s home phone is {1} work phone is {2}", e.name, e.homephone, e.workphone);
}
Console.WriteLine("Press <enter> to continue");
Console.ReadLine();
}
}
I'm trying to separate the home and work phone numbers in my query expressions.
However I'm only able to get the home phone number..
What am I doing wrong?
Element() method returns only the first element matching the specified name. So for each Employee you will only gets the first phone (Home).
A better approach is use Elements() method to gets ALL elements matching the specified name and then filters by attribute Type value:
var empquery = from e in xEle.Descendants("Employee")
select new
{
name = e.Element("Name").Value,
homephone = (string)e.Elements("Phone").Where(x => x.Attribute("Type").Value == "Home").FirstOrDefault().Value,
workphone = (string)e.Elements("Phone").Where(x => x.Attribute("Type").Value == "Work").FirstOrDefault().Value
};
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();
Hi below is a sample of the xml i am using. I been through allsorts of options I can think of to be able to start at the personData node and iterate the results, and nothing i seem to try works unless I navigate manually through each child node from the root down. Can anyone advise on how I can do this without starting at the root
My code currently is
using (var r = File.OpenText(#"C:\S\sp.xml"))
{
XPathDocument document = new XPathDocument(XmlReader.Create(r));
XPathNavigator xPathNav = document.CreateNavigator();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xPathNav.NameTable);
nsmgr.AddNamespace("g2", "http://person.transferobject.com/xsd");
XPathNodeIterator xni = xPathNav.Select("/g2:companys/g2:company/g2:person/g2:personData", nsmgr);
foreach (XPathNavigator nav in xni)
Console.WriteLine(nav.Name);
}
Xml
<?xml version="1.0" encoding="UTF-8"?>
<Header xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<dataSource xmlns="http://person.transferobject.com/xsd">IG2</dataSource>
<dateTime xmlns="http://person.transferobject.com/xsd">Thu Mar 21 15:56:42 GMT 2013</dateTime>
<formatVersion xmlns="http://person.transferobject.com/xsd">2.0</formatVersion>
<companys xmlns="http://person.transferobject.com/xsd">
<company>
<errorMessages xsi:nil="true"/>
<person>
<personData>
<address>
<address1 xmlns="http://transferobject.com/xsd">37 Smith St</address1>
<county xmlns="http://transferobject.com/xsd">COUNTY-37</county>
<postcode xmlns="http://transferobject.com/xsd">Po12 123</postcode>
</address>
<basicDetails>
<currentFirstName xmlns="http://transferobject.com/xsd">Fred</currentFirstName>
<currentLastName xmlns="http://transferobject.com/xsd">Bloggs</currentLastName >
<currentStage xmlns="http://transferobject.com/xsd">H1</currentStage>
<currentGroup xmlns="http://transferobject.com/xsd">3</currentGroup>
<dob xmlns="http://transferobject.com/xsd">2000-04-25</dob>
<email xmlns="http://transferobject.com/xsd">AN#AN.AOM</email>
<entryDate xmlns="http://transferobject.com/xsd">2003-09-03</entryDate>
</basicDetails>
</personData>
<personData>
<address>
<address1 xmlns="http://transferobject.com/xsd">37 Smith St</address1>
<county xmlns="http://transferobject.com/xsd">COUNTY-37</county>
<postcode xmlns="http://transferobject.com/xsd">Po12 123</postcode>
</address>
<basicDetails>
<currentFirstName xmlns="http://transferobject.com/xsd">John</currentFirstName>
<currentLastName xmlns="http://transferobject.com/xsd">Bloggs</currentLastName >
<currentStage xmlns="http://transferobject.com/xsd">H1</currentStage>
<currentGroup xmlns="http://transferobject.com/xsd">3</currentGroup>
<dob xmlns="http://transferobject.com/xsd">1999-04-25</dob>
<email xmlns="http://transferobject.com/xsd">AN#AN.AOM</email>
<entryDate xmlns="http://transferobject.com/xsd">2003-09-03</entryDate>
</basicDetails>
</personData>
</person>
</company>
</companys>
</header>
I know you're using XPath, but as you have an answer with XPath I'll give one using Linq
using System;
using System.Linq;
using System.Xml.Linq;
namespace xmlTest
{
class Program
{
static void Main()
{
XDocument doc = XDocument.Load("C:\\Users\\me\\Desktop\\so.xml");
var personDataDetails = (from p in doc.Descendants().Elements()
where p.Name.LocalName == "personData"
select p);
foreach (var item in personDataDetails)
{
Console.WriteLine(item.ToString());
}
Console.ReadKey();
}
}
}
Are you just asking how you can iterate through the personData nodes without listing the full path? If that's what you want to do, you can just do this:
XPathNodeIterator xni = xPathNav.Select("//g2:personData", nsmgr);