I have this xml string that I have to get a specific node
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Web;
using System.Xml;
using System.Xml.XPath;
//Xml string that has xml content
string xmlStr = "<Candidate>
<Content>
<DOB>14-Jan-1976</DOB>
<Employers>
<Employer>
<Name>Diane Trucking</Name>
<Addresses>
<Address>
<AddressLine1>1st Street</AddressLine1>
<AddressLine2/>
<City>First City</City>
<State>FL</State>
<Zip>12345</Zip>
</Address>
<Address>
<AddressLine1>1st Street</AddressLine1>
<AddressLine2/>
<City>Second City</City>
<State>FL</State>
<Zip>12346</Zip>
</Address>
<Address>
<AddressLine1>3rd Street</AddressLine1>
<AddressLine2/>
<City>Third City</City>
<State>FL</State>
<Zip>12347</Zip>
</Address>
</Addresses>
</Employer>
<Employer>
<Name>Tom Trucking</Name>
<Addresses>
<Address>
<AddressLine1>4th Street</AddressLine1>
<AddressLine2/>
<City>Fourth City</City>
<State>FL</State>
<Zip>12348</Zip>
</Address>
<Address>
<AddressLine1>5th Street</AddressLine1>
<AddressLine2/>
<City>Fifth City</City>
<State>FL</State>
<Zip>12349</Zip>
</Address>
<Address>
<AddressLine1>6th Street</AddressLine1>
<AddressLine2/>
<City>Sixth City</City>
<State>FL</State>
<Zip>12340</Zip>
</Address>
</Addresses>
</Employer>
</Employers>
</Content>
</Candidate>";
// Create xml Document
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlStr);
Navigator for alternative testing
XPathNavigator xPathNav = xmlDoc.CreateNavigator();
XPathExpression expr = XPathExpression.Compile("/Candidate/Content/Employers['1']/Employer['1']/Addresses['1']/Address");
Expression to read/Target xml specific xml node
XPathNodeIterator xPathIt = xPathNav.Select(expr);
if(xPathIt == null)
{
... xPathIt.Count is also 6
}
var xmlNodes1 = xmlDoc.SelectNodes("/Candidate/Content/Employers['1']/Employer['1']/Addresses['1']/Address");
for (int i = 0; i < xmlNodes1.Count; i++ )
{
...xmlNodes1.Count = 6. Returns all 6 address nodes , was expecting 3 nodes of "address" for the specific employers/employer/addresses/address
}
Both xPathIt as well as xmlNodes1 return 6 "Address" elements
I expected it to return 3 elements as there are 3 addresses for an employer
I wanted to target any element in the xml from root
Also, If I can do this I will be able to update the specific element.
Appreciate the help
Thank you
Try removing the single quotes from your XPath:
var xmlNodes1 = xmlDoc.SelectNodes("/Candidate/Content/Employers['1']/Employer['1']/Addresses['1']/Address");
should be
var xmlNodes1 = xmlDoc.SelectNodes("/Candidate/Content/Employers[1]/Employer[1]/Addresses[1]/Address");
Note the single quotes around the number 1 are removed.
The predicate for selecting the first item is [1]. Using the predicate ['1'] is ignored by SelectNodes so you are getting all nodes and not just the first.
Related
I have the following problem, I want to select the book with the author "Johnny Dapp33", which unfortunately does not work.
XML Code:
<employees xmlns:bar="http://www.bar.org">
<employee id="Test1">
<name>Johnny Dapp</name>
<author>Al Pacino</author>
</employee>
<employee id="Test2">
<name>Johnny Dapp33</name>
<author>Al Pacino</author>
</employee>
</employees>
I would have tried it via ".SelectSingleNode", unfortunately I always fail with the XPath.
Thank you for your help!
Let's say we have a file called Employees.xml in our project directory.
We can load the xml file in our memory by using this assignment:
XmlDocument doc = new XmlDocument();
doc.Load("Employees.xml");
Second we try to find a single node (presuambly) by its id in a structure employees/employee (this is our path), no we have to add the search param (id in this case) like this:
XmlNode singleNode = doc.SelectSingleNode("/employees/employee[#id='Test1']");
Console.WriteLine(singleNode.OuterXml);
However if we only know the name we are looking for we can also search for that specific value like this. We search in the employee node for the node value of name with the value of Johnny Dapp33:
XmlNode singleNode = doc.SelectSingleNode("descendant::employee[name='Johnny Dapp33']");
Console.WriteLine(singleNode.OuterXml);
While dealing with XMl, it is better to use LINQ to XML API.
It is available in the .Net Framework since 2007.
c#
void Main()
{
const string filePath = #"e:\Temp\WizardZZ.xml";
XDocument xdoc = XDocument.Load(filePath);
var employee = xdoc.Descendants("employee")
.Where(d => d.Elements("name").FirstOrDefault().Value.Equals("Johnny Dapp33"));
Console.WriteLine(employee);
}
Output
<employee id="Test2">
<name>Johnny Dapp33</name>
<author>Al Pacino</author>
</employee>
If you need to use XPath (and there must be a very strong reason for it), you can use it with XElement:
var xml = """
<employees xmlns:bar="http://www.bar.org">
<employee id="Test1">
<name>Johnny Dapp</name>
<author>Al Pacino</author>
</employee>
<employee id="Test2">
<name>Johnny Dapp33</name>
<author>Al Pacino</author>
</employee>
</employees>
""";
var x = XElement.Parse(xml);
var employees = x.XPathSelectElements("/employee[name='Johnny Dapp33']");
if (employees is not null)
{
foreach (var employee in employees)
{
WriteLine((string)employee.Element("name") ?? "[name] not found");
}
}
else
{
WriteLine("did not find any employees");
}
I need to add a new element "Location" to every parent node "Emp" after the element "ID".
<Record>
<Emp>
<ID>12</ID>
<Name>ABC</Name>
</Emp>
<Emp>
<ID>12</ID>
<Name>ABC</Name>
</Emp>
</Record>
I followed example and the steps in below answer by Erik Philips
Add an XML node to multiple parent nodes(which have same name)
XmlNodeList xNodeList = doc.SelectNodes("/Record/Emp");
foreach (XmlNode item in xNodeList)
{
XmlElement xNewChild = doc.CreateElement("Location");
xNewChild.InnerText = "USA";
item.AppendChild(xNewChild);
}
doc.Save(path);
Instead of this
item.AppendChild(xNewChild);
i added new statement
item.InsertAfter(xNewChild, doc.SelectSingleNode("Emp//ID"));
This inserts the new element right after Emp node starts and not after ID.
<Record>
<Emp>
<Location>USA</Location>
<ID>12</ID>
<Name>ABC</Name>
</Emp>
<Emp>
<Location>USA</Location>
<ID>12</ID>
<Name>ABC</Name>
</Emp>
</Record>
Can anyone suggest me where am i going wrong?
XmlDocument is an old API and has been superceded by XDocument, which provides a somewhat more pleasurable experience when dealing with XML.
Using XDocument you can:
var doc = XDocument.Parse(xmlString); //acquire your XDocument (perhaps by other means)
var targetElements = doc.XPathSelectElements("//Record/Emp/ID");
foreach (var element in targetElements)
{
var locationElement = new XElement("Location");
locationElement.Add("USA");
element.AddAfterSelf(locationElement);
}
My C# code:
XDocument doc = XDocument.Load(filename);
IEnumerable<XElement> collection =
doc.Elements("BCIRequest").Elements("Card").Elements("SelectedPIN");
My XML document:
<?xml version="1.0" encoding="utf-8"?>
<BCIRequest Version="2.0"
xmlns="urn:xxxxxx:bci:request">
<Header>
<SenderCode>XX99</SenderCode>
<SenderID>9999</SenderID>
<SequenceNumber>123</SequenceNumber>
<CardGroupCount>2</CardGroupCount>
<CardCount>4</CardCount>
<BlockCount>2</BlockCount>
</Header>
<!--card groups (must precede cards and blocks)-->
<CardGroup RequestID="1">
<CustomerNumber>XX01234567</CustomerNumber>
<CardGroupName Emboss="true">GROUP ONE</CardGroupName>
</CardGroup>
<CardGroup RequestID="2"
RequestRef="87416CB7-DAEF-483A-BD08-1A885531D958">
<CustomerNumber>XX12345678</CustomerNumber>
<CardGroupName Emboss="false">GROUP TWO</CardGroupName>
</CardGroup>
<Card RequestID="3">
<CustomerNumber>XX01234567</CustomerNumber>
<DriverCard>
<Driver Emboss="true">MARGE SIMPSON</Driver>
</DriverCard>
<CardTypeID>10</CardTypeID>
<PurchaseCategoryID>11</PurchaseCategoryID>
<Reissue>false</Reissue>
<GeneratedPIN/>
<OdoPrompt>false</OdoPrompt>
<CRNPrompt>false</CRNPrompt>
</Card>
<Card RequestID="4">
<CustomerNumber>XX12345678</CustomerNumber>
<VehicleCard>
<VRN Emboss="true">KYI 830</VRN>
</VehicleCard>
<CardTypeID>10</CardTypeID>
<PurchaseCategoryID>11</PurchaseCategoryID>
<Reissue>false</Reissue>
<SelectedPIN>0123</SelectedPIN>
<OdoPrompt>false</OdoPrompt>
<CRNPrompt>false</CRNPrompt>
</Card>
<Card RequestID="5">
<CustomerNumber>XX01234567</CustomerNumber>
<BearerCard>
<Bearer Emboss="true">OPEN XXXXXX</Bearer>
</BearerCard>
<CardTypeID>10</CardTypeID>
<PurchaseCategoryID>11</PurchaseCategoryID>
<Reissue>false</Reissue>
<FleetPIN/>
<OdoPrompt>false</OdoPrompt>
<CRNPrompt>false</CRNPrompt>
</Card>
<Block RequestID="6">
<CustomerNumber>XX01234567</CustomerNumber>
<PAN>7002999999999999991</PAN>
</Block>
<Card RequestID="7"
RequestRef="956EA6C5-7D7E-4622-94D0-38CAD9FCC8DF">
<CustomerNumber>XX01234567</CustomerNumber>
<DriverCard>
<Driver Emboss="true">HOMER SIMPSON</Driver>
<VRN Emboss="true">795 DVI</VRN>
</DriverCard>
<EmbossText>SPRINGFIELD POWER</EmbossText>
<CardTypeID>10</CardTypeID>
<TokenTypeID>20</TokenTypeID>
<PurchaseCategoryID>30</PurchaseCategoryID>
<ExpiryDate>2018-12</ExpiryDate>
<Reissue>true</Reissue>
<SelectedPIN>0123</SelectedPIN>
<OdoPrompt>true</OdoPrompt>
<CRNPrompt>true</CRNPrompt>
<!--address with optional fields specified-->
<CardDeliveryAddress OneTimeUse="false">
<ContactName>M xxxx</ContactName>
<ContactTitle>Mr</ContactTitle>
<CompanyName>Sxxxx</CompanyName>
<Line1>Sector 22-F</Line1>
<Line2>Springfield Power Plant</Line2>
<Line3>xxx Road</Line3>
<City>xxxx</City>
<Zipcode>xxxx</Zipcode>
<CountryCode>xxx</CountryCode>
</CardDeliveryAddress>
<!--address with only required fields-->
<PINDeliveryAddress OneTimeUse="true">
<Line1>xxxx</Line1>
<City>xxx</City>
<Zipcode>xxxx</Zipcode>
<CountryCode>xxxx</CountryCode>
</PINDeliveryAddress>
<Limits>
<Value Transaction="unlimited" Daily="200" Weekly="unlimited" Monthly="400"/>
<Volume Transaction="100" Daily="unlimited" Weekly="unlimited" Monthly="unlimited"/>
<Transactions Daily="unlimited" Weekly="unlimited" Monthly="unlimited"/>
<Day Monday="true" Tuesday="true" Wednesday="true" Thursday="true" Friday="true" Saturday="false" Sunday="false"/>
<Time Start="unlimited" End="17:00:00"/>
</Limits>
<Products>
<FuelProductRestrictionID>40</FuelProductRestrictionID>
<NonFuelProductRestrictionID>51</NonFuelProductRestrictionID>
<NonFuelProductRestrictionID>52</NonFuelProductRestrictionID>
<NonFuelProductRestrictionID>53</NonFuelProductRestrictionID>
<NonFuelProductRestrictionID>54</NonFuelProductRestrictionID>
<NonFuelProductRestrictionID>55</NonFuelProductRestrictionID>
</Products>
</Card>
<Block RequestID="8"
RequestRef="69A3E44D-DC10-4BEE-9249-1FC3C651BA0E">
<CustomerNumber>xxxxx</CustomerNumber>
<PAN>xxxxxx</PAN>
</Block>
</BCIRequest>
I need to update the element value in the above values. The old value is:
<SelectedPIN>0123</SelectedPIN>
And the new value should be:
<SelectedPIN EncryptedPIN="TKDS" FormNumber="000793906306">****</SelectedPIN>
Can anyone can help me on this?
If I selected the BCIRequest element, it's returning a null value. I've tried many solutions but unable to get one working on this XML file.
There many ways an Xml can be be modified, I prefer XDocument
XDocument doc = XDocument.Parse(input);
foreach (var element in doc.Descendants("SelectedPIN")) // filter if you want single element, in example I modifed for all elements.
{
element.Add(new XAttribute("EncryptedPIN", "TKDS"));
element.Add(new XAttribute("FormNumber", "000793906306"));
element.Value = "xxxxx"; //new value
}
and finally you can save the document using
doc.Save();
Take a look at this Demo
The root node (BCIRequest) contains a namespace so you need to include that into your query. Something like this should work:
XNamespace ns = "urn:xxxxxx:bci:request";
IEnumerable<XElement> collection = doc.Elements(ns + "BCIRequest").Elements(ns + "Card").Elements(ns + "SelectedPIN");
This is my xml and I want to get the value of the
of the last friend(than means only want the <fname>5</fname>'s <streetname></streetname>). how can I do that. I used XElement with Last(), but it didn't work for me.
<start>
<option>
<name></name>
<gender></gender>
<hometown>
<street1></street1>
<street2></street2>
</hometown>
<friend>
<fname>1</fname>
<lname></lname>
<address>
<number></number>
<streetname></streetname>
</address>
</friend>
<friend>
<fname>2</fname>
<lname></lname>
<address>
<number></number>
<streetname></streetname>
</address>
</friend>
<friend>
<fname>3</fname>
<lname></lname>
<address>
<number></number>
<streetname></streetname>
</address>
</friend>
<friend>
<fname>4</fname>
<lname></lname>
<address>
<number></number>
<streetname></streetname>
</address>
</friend>
<friend>
<fname>5</fname>
<lname></lname>
<address>
<number></number>
<streetname></streetname>
</address>
</friend>
</option>
</start>
this is my code.
XElement rootele = XElement.Load(Server.MapPath("~/XmlFiles/CruiseData/cruiseprodutstwo.xml"));
var getneededData = rootele.Elements("CruiseProduct")
.Where(l => l.Element("Location").Value == destination && l.Element("Types").Element("Type").Value == cruisetype);
foreach (var itm in getneededData)
{
d.Add(new Details
{
numberofDays = itm.Element("Itinerary").Element("ItineraryItem").Element("EndDay")//I want it in here
});
}
Use Elements() instead of singular Element(). The latter always return the first matched element without any additional method call, but you want the last here :
numberofDays = itm.Elements("Itinerary").Elements("ItineraryItem").Elements("EndDay").Last();
You can use XPath to extract this value:
using System.Xml.Linq;
using System.Xml.XPath;
XDocument xDocument = XDocument.Parse(xmlString);
string lastStreetname = xDocument
.XPathSelectElement("start/option/friend[last()]/address/streetname")
.Value;
Note that your XML is invalid. It should be a closing </option> tag in the end of your XML instead of <option>.
Is there a way to take a spreadsheet and turn it into xml file below?
<?xml version="1.0"?>
<ArrayOfBusiness xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Business>
<Name>Company Name 1</Name>
<AddressList>
<Address>
<AddressLine>123 Main St.</AddressLine>
</Address>
</AddressList>
</Business>
<Business>
<Name>Company Name 2</Name>
<AddressList>
<Address>
<AddressLine>1 Elm St.</AddressLine>
</Address>
<Address>
<AddressLine>2 Elm St.</AddressLine>
</Address>
</AddressList>
</Business>
</ArrayOfBusiness>
I put this in LinqPad and it did what you needed. If you've never used LinqPad... well now's a great time to start.
var csvs = new List<string>();
csvs.Add( "Company Name 1,123 Main St.");
csvs.Add("Company Name 2,1 Elm St.,2 Elm St.");
var xml =
(from c in csvs
let split = c.Split(',')
select // split
new XElement("ArrayOfBusiness",
new XElement("Business",
new XElement("Name", split[0] ),
new XElement("AddressList",
new XElement("Address"
,
(from s in split.Skip(1) // skip the first split
select
new XElement("AddressLine", s))
)
)))); // <-- is it LISP?
xml.Dump();
Results:
<ArrayOfBusiness>
<Business>
<Name>Company Name 1</Name>
<AddressList>
<Address>
<AddressLine>123 Main St.</AddressLine>
</Address>
</AddressList>
</Business>
<Business>
<Name>Company Name 2</Name>
<AddressList>
<Address>
<AddressLine>1 Elm St.</AddressLine>
<AddressLine>2 Elm St.</AddressLine>
</Address>
</AddressList>
</Business>
</ArrayOfBusiness>
It isn't exactly what you wanted, but looks functionally equivalent to me. Might need a bit of tweaking in the LINQ.
Write to file with: File.WriteAllText(#"c:\temp\addresses.xml", xml.ToString());
Parsing the .csv file into 'Business' objects should be straightforward.
Its then a simple case of using the XmlSerializer class to generate the xml.
I would say yes, but with out seeing the CSV file it is hard to say.
If your CSV was somthing like this:
Name, Address1, Address2
Company Name 1,123 Main St.,
Company Name 2,1 Elm St.,1 Elm St.
you could easily parse this into a class.
class Business
{
public string Name { get; set; }
public List<Address> AddressList { get; set; }
}
class Address
{
public string AddressLine { get; set; }
}
(untested)