XDocument losing line number - c#

I'm using XDocument to parse my XML File, but when I try to read the line number of a XNode or a XElement, it's always equal to zero.
I tried different ways to parse it:
foreach (XElement node in xDoc.Root.Descendants("nodeName"))
or
xDoc.XPathSelectElement("nodeName")
and each time ((IXmlLineInfo)node).LineNumber returns 0!
I'm using a XmlNamespaceManager.
Did I miss something?
Thanks in advance!
Edit :
Here is the concerned Xml as asked.
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.008.001.02" xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance">
<CstmrDrctDbtInitn>
<GrpHdr>
<MsgId>XXXXXXXXX</MsgId>
<CreDtTm>2013-06-12T00:00:00</CreDtTm>
<NbOfTxs>1</NbOfTxs>
<CtrlSum>136.82</CtrlSum>
<InitgPty>
<Nm>name</Nm>
<Id>
<OrgId>
<Othr>
<Id>XXXXXXXXX</Id>
</Othr>
</OrgId>
</Id>
</InitgPty>
</GrpHdr>
<PmtInf>
<PmtInfId>275-20130612-FIRST</PmtInfId>
<PmtMtd>DD</PmtMtd>
<BtchBookg>true</BtchBookg>
<NbOfTxs>1</NbOfTxs>
<CtrlSum>136.82</CtrlSum>
<PmtTpInf>
<SvcLvl>
<Cd>SEPA</Cd>
</SvcLvl>
<LclInstrm>
<Cd>CORE</Cd>
</LclInstrm>
<SeqTp>RCUR</SeqTp>
</PmtTpInf>
<ReqdColltnDt>2013-06-05</ReqdColltnDt>
<Cdtr>
<Nm>name</Nm>
<PstlAdr>
<Ctry>BE</Ctry>
<AdrLine>XXXXXXXXX</AdrLine>
<AdrLine>XXXXXXXXX</AdrLine>
</PstlAdr>
</Cdtr>
<CdtrAcct>
<Id>
<IBAN>XXXXXXXXX</IBAN>
</Id>
</CdtrAcct>
<CdtrAgt>
<FinInstnId>
<BIC>XXXXXXXXX</BIC>
</FinInstnId>
</CdtrAgt>
<ChrgBr>SLEV</ChrgBr>
<CdtrSchmeId>
<Id>
<PrvtId>
<Othr>
<Id>XXXXXXXXX</Id>
<SchmeNm>
<Prtry>SEPA</Prtry>
</SchmeNm>
</Othr>
</PrvtId>
</Id>
</CdtrSchmeId>
<DrctDbtTxInf>
<PmtId>
<InstrId>XXXXXXXXX</InstrId>
<EndToEndId>XXXXXXXXX</EndToEndId>
</PmtId>
<InstdAmt Ccy="EUR">136.82</InstdAmt>
<DrctDbtTx>
<MndtRltdInf>
<MndtId>XXXXXXXXX</MndtId>
<DtOfSgntr>2009-10-31</DtOfSgntr>
<AmdmntInd>false</AmdmntInd>
<AmdmntInfDtls>
<AmdmntInd>yellowland</AmdmntInd>
<OrgnlMndtId>XXXXXXXXX</OrgnlMndtId>
<OrgnlCdtrSchmeId>
<Id>
<PrvtId>
<Othr>
<Id>XXXXXXXXX</Id>
<SchmeNm>
<Prtry>SEPA</Prtry>
</SchmeNm>
</Othr>
</PrvtId>
</Id>
</OrgnlCdtrSchmeId>
</AmdmntInfDtls>
</MndtRltdInf>
</DrctDbtTx>
<DbtrAgt>
<FinInstnId>
<BIC>XXXXXXXXX</BIC>
</FinInstnId>
</DbtrAgt>
<Dbtr>
<Nm>TEST</Nm>
<PstlAdr>
<Ctry>BE</Ctry>
<AdrLine>XXXXXXXXX</AdrLine>
<AdrLine>XXXXXXXXX</AdrLine>
</PstlAdr>
</Dbtr>
<DbtrAcct>
<Id>
<IBAN>XXXXXXXXX</IBAN>
</Id>
</DbtrAcct>
<RmtInf>
<Ustrd>test</Ustrd>
</RmtInf>
</DrctDbtTxInf>
</PmtInf>
</CstmrDrctDbtInitn>
</Document>

The LineInformations are not always loaded, when you load xml via XDocument.
You need to specify that you also want to load the LineInformation when you load the XML. That is done by using one of the Load methods that you can pass in a value of LoadOptions of the XDocument class.
var document = XDocument.Load(file, LoadOptions.SetLineInfo);

From here
XDocument xdoc = XDocument.Load(file);
IEnumerable<XElement> nodes = xdoc.Descendants("nodeName");
foreach (XElement node in nodes)
{
IXmlLineInfo info = node;
int lineNumber = info.LineNumber;
}

Related

SelectNodes brings back to many results

Loading this XML works
<?xml version="1.0" encoding="utf-8" ?>
<export>
<document ID="uuuid_1">
<Property Name="PersonName" Value="bob"></Property>
<Property Name="FileName" Value="bob.tif">
<Reference Link="company\export\uuuid_1_423_bob.tif"/>
</Property>
<Property Name="FileName" Value="bob.txt">
<Reference Link="company\export\uuuid_1_123_bob.txt"/>
</Property>
<Property Name="FileName" Value="bob.tif">
<Reference Link="company\export\uuuid_1_123_bob.tif"/>
</Property>
</document>
<document ID="uuuid_2">
<Property Name="PersonName" Value="mary"></Property>
<Property Name="FileName" Value="mary.tif">
<Reference Link="company\export\uuuid_2_456_mary.tif"/>
</Property>
<Property Name="FileName" Value="mary.txt">
<Reference Link="company\export\uuuid_2_567_mary.txt"/>
</Property>
</document>
</export>
with that method
static void XmlLoader(string xml_path)
{
Console.WriteLine("Loading " + xml_path);
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(xml_path);
XmlNodeList nodes_document = xmldoc.SelectNodes("/export/document");
foreach (XmlNode nd in nodes_document)
{
string Id = nd.Attributes["ID"].Value.ToString();
string name = nd.SelectSingleNode("//Property[#Name='PersonName']/#Value").InnerText;
XmlNodeList files = nd.SelectNodes("//Property[#Name='FileName'][contains(#Value,'.tif')]/Reference/#Link");
Console.WriteLine(files.ToString());
}
}
The XmlNodeList inside the iteration of documents brings back a list of ALL tifs in the XML not only the ones from the nd Node.
How would I correctly use Xpath to select a list inside the nd element?
Just remove "//" from SelectNodes and SelectSingleNode. The double slash is parsing the complete xml
static void XmlLoader(string xml_path)
{
Console.WriteLine("Loading " + xml_path);
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(xml_path);
XmlNodeList nodes_document = xmldoc.SelectNodes("/export/document");
foreach (XmlNode nd in nodes_document)
{
string Id = nd.Attributes["ID"].Value.ToString();
string name = nd.SelectSingleNode("Property[#Name='PersonName']/#Value").InnerText;
XmlNodeList files = nd.SelectNodes("Property[#Name='FileName'][contains(#Value,'.tif')]/Reference/#Link");
Console.WriteLine(files.ToString());
}
}

Change XML element value in c#

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");

NullReferenceException unhandled by user code while changing XML document

I am getting the error : NullReferenceException was unhandled by user code , object reference not set to an instance of an object. While I want to change the element of an xml file. this is the code the error is generated on :
doc2.SelectSingleNode("/Document/CstmrCdtTrfInitn/GrpHdr/MsgId", nsmgr).InnerText = bank.Afkorting + "-0001";
This is the previous code :
XmlDocument doc2 = new XmlDocument();
doc2.Load(#"C:\Users\Kevin\Desktop\xsd\betalingen_sepa.xml");
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc2.NameTable);
nsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
DagboekFinancieel bank = os.FindObject<DagboekFinancieel>(CriteriaOperator.Parse("[Omschrijving] = ?", opdracht.Bank));
doc2.SelectSingleNode("/Document/CstmrCdtTrfInitn/GrpHdr/MsgId", nsmgr).InnerText = bank.Afkorting + "-0001";
string naamFile = bank.Afkorting + "-0001";
doc2.Save(#"C:\" + naamFile + ".xml");
This is the original XML file I got :
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03">
<CstmrCdtTrfInitn>
<GrpHdr>
<MsgId>#ref_xml#</MsgId>
<CreDtTm>#datum_tijd_xml#</CreDtTm>
<NbOfTxs>#aantal_transacties#</NbOfTxs>
<InitgPty>
<Nm>#afzender_naam#</Nm>
<Id>
<OrgId>
<Othr>
<Id>#afzender_btw_nr#</Id>
<Issr>KBO-BCE</Issr>
</Othr>
</OrgId>
</Id>
</InitgPty>
</GrpHdr>
<PmtInf>
<PmtInfId>#ref_payment_block#</PmtInfId>
<PmtMtd>TRF</PmtMtd>
<BtchBookg>true</BtchBookg>
<PmtTpInf>
<SvcLvl>
<Cd>SEPA</Cd>
</SvcLvl>
</PmtTpInf>
<ReqdExctnDt>#datum_uitvoering#</ReqdExctnDt>
<Dbtr>
<Nm>#afzender_naam#</Nm>
</Dbtr>
<DbtrAcct>
<Id>
<IBAN>#afzender_iban#</IBAN>
</Id>
</DbtrAcct>
<DbtrAgt>
<FinInstnId>
<BIC>#afzender_bic#</BIC>
</FinInstnId>
</DbtrAgt>
<ChrgBr>#kosten#</ChrgBr>
<CdtTrfTxInf>
<PmtId>
<EndToEndId>#ref_end_to_end#</EndToEndId>
</PmtId>
<Amt>
<InstdAmt Ccy="#munt#">#bedrag#</InstdAmt>
</Amt>
<CdtrAgt>
<FinInstnId>
<BIC>#leveran_bic#</BIC>
</FinInstnId>
</CdtrAgt>
<Cdtr>
<Nm>#leveran_naam#</Nm>
<PstlAdr>
<Ctry>#leveran_land#</Ctry>
<AdrLine>#leveran_straat#</AdrLine>
<AdrLine>#leveran_wpl#</AdrLine>
</PstlAdr>
</Cdtr>
<CdtrAcct>
<Id>
<IBAN>#leveran_iban#</IBAN>
</Id>
</CdtrAcct>
<RmtInf>
<Ustrd>#leveran_ref#</Ustrd>
<Strd>
<CdtrRefInf>
<Tp>
<CdOrPrtry>
<Cd>#gestruct_mededeling_soort1#</Cd>
</CdOrPrtry>
<Issr>#gestruct_mededeling_soort2#</Issr>
</Tp>
<Ref>#gestruct_mededeling#</Ref>
</CdtrRefInf>
</Strd>
</RmtInf>
</CdtTrfTxInf>
</PmtInf>
</CstmrCdtTrfInitn>
</Document>
Your xml namespace is not correct. Use it like
nsmgr.AddNamespace("ns", "urn:iso:std:iso:20022:tech:xsd:pain.001.001.03");
doc2.SelectSingleNode("/ns:Document/ns:CstmrCdtTrfInitn/ns:GrpHdr/ns:MsgId", nsmgr).InnerText = "some text";

insert element after selected node

Am trying to add a new element called entity after the last entity but it keeps adding it inside the selected entity. To understand better this is my xml sample.
<Root>
<Class Name="ECMInstruction" Style="Top">
<Entity Id="1" Name="DocumentInformation" />
<Entity Id="2" Name="CustomerInformation" />
<Property Id="1" Name="DocumentTitle">
</Property>
<Property Id="2" Name="DateCreated">
<Lists>
<ListName>ws_Users</ListName>
<ListName>dfdfdfd</ListName>
</Lists>
</Property>
<Property Id="3" Name="Deadline">
</Property>
</Class>
</Root>
This is how it looks like after is inserted. I've tried using insertAfter but it gives me error.
<Entity Id="1" Name="DocumentInformation">
<Entity Id="2" Name="sds" />
</Entity>
The code:
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load("sample.xml");
XmlNodeList cnode = xmldoc.DocumentElement.SelectNodes("//Class[#Name='" + CurrentClass + "']/Entity");
foreach (XmlNode c in cnode)
{
int value = 0;
String entitycount = cnode.Count.ToString();
int.TryParse(entitycount, out value);
value = value + 1;
XmlElement root = xmldoc.CreateElement("Entity");
root.SetAttribute("Id", value.ToString());
root.SetAttribute("Name", EntityNametxt.Text);
c.AppendChild(root);
xmldoc.Save("sample.xml");
}
"I've tried using insertAfter but it gives me error. "
As per documentation, InsertAfter() should be called on parent node of referenced XmlNode (the 2nd argument of the method), otherwise ArgumentException will be thrown :
//instead of this : c.AppendChild(root);
//..you could do as follow :
c.ParentNode.InsertAfter(root, c);

delete child node with specific value

Am trying to delete a list name containing the value "dfdfdfd" from an XPath
XmlNode names = LoadDocument(xml).DocumentElement.SelectSingleNode("//Class[#Name='" + getCurClass() + "']/Property[#Id='" + i + "']/Lists[contains(ListName,'ws_Users')]");
after I execute this statement:
names.RemoveChild(names.FirstChild);
but I nothing happens.
My XML :
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Class Name="ECMInstruction" Style="Top">
<Entity Id="1" Name="DocumentInformation" />
<Property Id="1">
</Property>
<Property Id="2">
<Lists>
<ListName>ws_Users</ListName>
<ListName>dfdfdfd</ListName>
</Lists>
</Property>
</Class>
</Root>
Thanks for the help
You need to save the modified XmlDocument object back to file :
XmlDocument doc = LoadDocument(xml);
XmlNode names = doc.DocumentElement.SelectSingleNode("//Class[#Name='" + getCurClass() + "']/Property[#Id='" + i + "']/Lists[contains(ListName,'ws_Users')]");
names.RemoveChild(names.FirstChild);
//save `doc` back to file :
doc.Save("path_to_the_xml_file.xml");
or this way :
XmlNode names = LoadDocument(xml).DocumentElement.SelectSingleNode("//Class[#Name='" + getCurClass() + "']/Property[#Id='" + i + "']/Lists[contains(ListName,'ws_Users')]");
names.RemoveChild(names.FirstChild);
//save owner `XmlDocument` back to file :
names.OwnerDocument.Save("path_to_the_xml_file.xml");

Categories