Below is the XML I am trying to parse. I need to extract the DLRNUMBER tag from the first FL nodelist (first one). I am trying to use Xdocument. I don't know the syntax to get the tag.
XDocument xdoc = XDocument.Load(#"C:\Temp\FR_in.xml");
var query = from e in xdoc.Descendants("FLSS")
select e;
Below is the XML:
<PACK Id="NI">
<PROP Id="KEY1" Val="NI_NY" />
<PROP Id="SOURCE" Val="NI" />
<PROP Id="TARGET" Val="NY" />
<REQUEST Id="CREATE_FR">
<PROP Id="PROID" Val="LIBRARY.OBJECT" />
<DOBJ Id="FH"> <ATTR Id="PAYTYPE" Val="WR" />
<ATTR Id="DTREATED" Val="3/20/2018" />
<ATTR Id="DUEDATE" Val="3/20/2018" />
<ATTR Id="AMOUNT" Val="1499.5" />
<ATTR Id="SOURCE" Val="DS" />
<ATTR Id="CREATOR" Val="DSI" />
<ATTR Id="APPROVER" Val="UF03567" />
<COLL Id="FLSS">
<FL>
<DOBJ Id="FL_1">
<ATTR Id="ACCTNUMBER" Val="162101" />
<ATTR Id="CENTER" Val="506" />
<ATTR Id="DLRNUMBER" Val="48" />
<ATTR Id="DLR" Val="58D" />
<ATTR Id="PAYEE" Val="58D" />
<ATTR Id="PAYMENTTYPE" Val="WR" />
<ATTR Id="AMOUNT" Val="1499.5" />
</DOBJ>
<DOBJ Id="FL_2">
<ATTR Id="ACCTNUMBER" Val="194061" />
<ATTR Id="CENTER" Val="506" />
<ATTR Id="DLRNUMBER" Val="48" />
<ATTR Id="DLR" Val="58D" />
<ATTR Id="PAYEE" Val="58D" />
<ATTR Id="PAYMENTTYPE" Val="WR" />
<ATTR Id="AMOUNT" Val="1499.5" />
</DOBJ>
</FL>
</COLL>
</DOBJ>
</REQUEST>
</PACK>
In Descendants you have to provide the XName of the nodes to search for. In your code example you try to search for a value of an attribute of a node.
You can do this:
var result = xdoc.Descendants("ATTR")
.FirstOrDefault(element =>
element.Attribute("Id")?.Value == "DLRNUMBER")?.Attribute("Val")?.Value;
This finds the first ATTR tag that has an Id attribute with the value DLRNUMBER and returns the value of its Val attribute.
If there may be other DLRNUMBER values at different levels that you don't want to find, you can consider to find the COLL node first:
var collNode = xdoc.Descendants("COLL").FirstOrDefault();
var result = collNode.Descendants("ATTR")
.FirstOrDefault(element =>
element.Attribute("Id")?.Value == "DLRNUMBER")?.Attribute("Val")?.Value;
or refine the search according to your requirements and the kind of xml you expect as input.
Try following. I prefer to get all items in an array so I can select one or more items as needed. :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication31
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement fl = doc.Descendants("FL").FirstOrDefault();
int[] dlrNumbers = fl.Elements("DOBJ").Select(x => x.Elements("ATTR").Where(y => (string)y.Attribute("Id") == "DLRNUMBER").Select(y => (int)y.Attribute("Val"))).SelectMany(y => y).ToArray();
}
}
}
Related
I am struggling with this situations for a while!! and i really hope i can get some help from you.
in a method i am receiving this XML as a string :
<?xml version="1.0" encoding="utf-16" standalone="no"?>
<VC_001_CreateDocument>
<VCRequest>
<Header>
<OrganisationData>
<ClientId />
<UserId />
<Pass />
</OrganisationData>
<Article>
<OutcomeSource>ERP</OutcomeSource>
<ArticleNumber>6034967-Sample</ArticleNumber>
<ProductNumbers>
<ProductNumber Type="GTIN" Level="PRI" />
</ProductNumbers>
<Forecast />
<ERPStatus>APP</ERPStatus>
<SerialisationFlag />
<CSDBArticleNumber>6034967-Sample</CSDBArticleNumber>
<ArticleDescription>Rose</ArticleDescription>
<WorkflowId />
<CommonName />
<PharmaceuticalForm />
<Strength />
<PackageWeight />
<PackageSize />
<PackageType />
<GS1GLN />
<GS1CompanyPrefix />
<Customer>
<CustomerId />
<CustomerErpNumber />
</Customer>
<ShelfLife />
<Region />
<ProductionSites>
<ProductionSite>
<ProductionSiteId />
<ProductionSiteErpNumber />
</ProductionSite>
</ProductionSites>
<GenericArticleField01 />
<GenericArticleField02 />
<GenericArticleField03 />
<GenericArticleField04 />
<GenericArticleField05 />
<RequiredFields>
<RequiredField Name="" />
</RequiredFields>
<Comment />
<Checked />
<TargetMarkets>
<TargetMarket>
<GS1NHRN />
<GenericFields Type="" Language="" />
<Mah />
<Wholesaler />
</TargetMarket>
</TargetMarkets>
<VerificationSystem />
<Email />
<FixData />
<StartValueInitial />
<SubPools>
<SubPool>
<AggregationLevel />
<PoolIdentProductNumber />
<QuantityPerLevel />
<IncompletePackagingRule />
<QuantityOfLayer />
<GenerationPattern />
<PostProductionSerialNumberAssignment />
<PrePrinting />
<Factor />
<Threshold />
<SerialNumberType />
<ExtensionDigit />
<SerialNumberSource />
<DeliveranceInformation>
<MinimumValue />
<PercentualAmount />
</DeliveranceInformation>
</SubPool>
</SubPools>
<Report>
<ReportExternal />
<ReportVerificationSystem />
</Report>
</Article>
</Header>
</VCRequest>
</VC_001_CreateDocument>
to transform in an Envelope to use in my SOAP Request exactly like this: reading the node values of course if they have!
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:csdb="http://site.de/csdb">
<soapenv:Header/>
<soapenv:Body>
<csdb:VC_001_CreateDocument>
<csdb:VARequest>
<csdb:Header>
<csdb:OrganisationData>
<csdb:ClientId>EDP</csdb:ClientId>
<csdb:UserId>webservice</csdb:UserId>
<csdb:Pass>!password*</csdb:Pass>
</csdb:OrganisationData>
</csdb:Header>
<csdb:Article>
<csdb:OutcomeSource>ERP</csdb:OutcomeSource>
<csdb:ArticleNumber>6034967-Sample</csdb:ArticleNumber>
<csdb:ProductNumbers>
<csdb:ProductNumber type="GTIN" level="PRI"></csdb:ProductNumber>
</csdb:ProductNumbers>
<csdb:Forecast></csdb:Forecast>
<csdb:ERPStatus></csdb:ERPStatus>
<csdb:SerialisationFlag></csdb:SerialisationFlag>
<csdb:CSDBArticleNumber>6034967-AMOSTRA</csdb:CSDBArticleNumber>
<csdb:ArticleDescription>PINOX ROSA</csdb:ArticleDescription>
<csdb:WorkflowId></csdb:WorkflowId>
<csdb:CommonName></csdb:CommonName>
<csdb:PharmaceuticalForm></csdb:PharmaceuticalForm>
<csdb:Strength></csdb:Strength>
<csdb:PackageWeight></csdb:PackageWeight>
<csdb:PackageSize></csdb:PackageSize>
<csdb:PackageType></csdb:PackageType>
<csdb:GS1GLN></csdb:GS1GLN>
<csdb:GS1CompanyPrefix></csdb:GS1CompanyPrefix>
<csdb:Customer>
<csdb:Customer></csdb:Customer>
<csdb:CustomerId></csdb:CustomerId>
<csdb:CustomerErpNumber></csdb:CustomerErpNumber>
</csdb:Customer>>
<csdb:ShelfLife></csdb:ShelfLife>
<csdb:Region></csdb:Region>
<csdb:ProductionSites>
<csdb:ProductionSite>
<csdb:ProductionSiteId></csdb:ProductionSiteId>
<csdb:ProductionSiteErpNumber></csdb:ProductionSiteErpNumber>
</csdb:ProductionSite>
</csdb:ProductionSites>
<csdb:GenericArticleField01></csdb:GenericArticleField01>
<csdb:GenericArticleField02></csdb:GenericArticleField02>
<csdb:GenericArticleField03></csdb:GenericArticleField03>
<csdb:GenericArticleField04></csdb:GenericArticleField04>
<csdb:GenericArticleField05></csdb:GenericArticleField05>
<csdb:RequiredFields>
<csdb:RequiredField name="?"></csdb:RequiredField>
</csdb:RequiredFields>
<csdb:Comment></csdb:Comment>
<csdb:Checked></csdb:Checked>
<csdb:GS1NHRN GS1NHRNNational="?"></csdb:GS1NHRN>
<csdb:TargetMarkets>
<csdb:TargetMarket targetMarket="?">
<csdb:GS1NHRN GS1NHRNNational="?"></csdb:GS1NHRN>
<csdb:GenericFields type="?" language="?"></csdb:GenericFields>
<csdb:Mah></csdb:Mah>
<csdb:Wholesaler></csdb:Wholesaler>
</csdb:TargetMarket>
</csdb:TargetMarkets>
<csdb:Verificationsystem></csdb:Verificationsystem>
<csdb:Email></csdb:Email>
<csdb:FixData></csdb:FixData>
<csdb:StartValueInitial></csdb:StartValueInitial>
<csdb:Subpools>
<csdb:Subpool>
<csdb:AggregationLevel></csdb:AggregationLevel>
<csdb:PoolIdentProductNumber type="?"></csdb:PoolIdentProductNumber>
<csdb:QuantityPerLevel></csdb:QuantityPerLevel>
<csdb:IncompletePackagingRule></csdb:IncompletePackagingRule>
<csdb:QuantityOfLayer></csdb:QuantityOfLayer>
<csdb:GenerationPattern></csdb:GenerationPattern>
<csdb:PostProductionSerialNumberAssignment></csdb:PostProductionSerialNumberAssignment>
<csdb:PrePrinting></csdb:PrePrinting>
<csdb:Factor></csdb:Factor>
<csdb:Threshold></csdb:Threshold>
<csdb:SerialNumberType></csdb:SerialNumberType>
<csdb:ExtensionDigit></csdb:ExtensionDigit>
<csdb:SerialNumberSource></csdb:SerialNumberSource>
<csdb:DeliveranceInformation>
<csdb:MinimumValue></csdb:MinimumValue>
<csdb:PercentualAmount></csdb:PercentualAmount>
</csdb:DeliveranceInformation>
</csdb:Subpool>
</csdb:Subpools>
<csdb:Report>
<csdb:ReportExternal></csdb:ReportExternal>
<csdb:ReportVerificationSystem></csdb:ReportVerificationSystem>
</csdb:Report>
</csdb:Article>
</csdb:VCRequest>
</csdb:VC_001_CreateDocument>
</soapenv:Body>
</soapenv:Envelope>
THis is exactly the XML that i receive and that's the envelope that i will have to create (Request generated by SOAPUI),the nodes will have dynamic Values of course, those parameteres were just an example.
i assume this is the easiest way since i can't "add service reference" because i am trying to develop a DLL where the webservices will be nested. There's no web.config where this DLL is going to be installed.
Thank you soo much for helping me with the logic for this problem that is driving me crazy
Using xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
StreamReader reader = new StreamReader(FILENAME);
reader.ReadLine(); // skip the utf-16 in header that Net Library doesn't accept
XDocument doc = XDocument.Load(reader);
string soapHeader = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:csdb=\"http://site.de/csdb\">" +
"<soapenv:Header/>" +
"<soapenv:Body>" +
"</soapenv:Body>" +
"</soapenv:Envelope>";
XElement soap = XElement.Parse(soapHeader);
XNamespace nsCsdb = soap.GetNamespaceOfPrefix("csdb");
XNamespace nsSoapenv = soap.GetNamespaceOfPrefix("soapenv");
XElement body = soap.Descendants(nsSoapenv + "Body").FirstOrDefault();
body.Add(doc.Root);
foreach (XElement child in body.Descendants())
{
child.Name = nsCsdb.GetName(child.Name.LocalName);
List<XAttribute> atList = child.Attributes().ToList();
child.Attributes().Remove();
foreach (XAttribute at in atList)
child.Add(new XAttribute(nsCsdb.GetName(at.Name.LocalName), at.Value));
}
}
}
}
I am trying to remove some specified attributed from the XML file sample code was below. string[] szNodeList is the array list so node contains name in string array will be removed ans save again
Any help will be appreciated.
var doc = new System.Xml.XmlDocument();
doc.Load("attrs.xml");
var root = doc.DocumentElement;
string[] szNodeList = new string[] { "titleTextColor"
,"isLightTheme"
,"showText"
};
foreach (System.Xml.XmlElement child in root )
{
foreach (string sz in szNodeList)
{
root.RemoveAttribute(sz);
//if (child.Attributes[sz] != null)
//{
// child.Attributes.Remove(child.Attributes[sz]);
//}
}
}
doc.Save("build.xml");
XML CODE
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="cropImageStyle" format="reference" />
<attr name="drawerArrowStyle" format="reference" />
<attr name="height" format="dimension" />
<attr name="isLightTheme" format="boolean" />
<attr name="title" format="string" />
<attr name="navigationMode">
<enum name="listMode" value="1" />
<enum name="normal" value="0" />
<enum name="tabMode" value="2" />
</attr>
</resources>
But saving as original file without changes i thing remove is not working.
Try this:
doc
// select all `resources/attr` node
.SelectNodes("resources/attr")
.Cast<XmlNode>()
// that contains the `name` attribute whose value is in `szNodeList`
.Where(x => !string.IsNullOrEmpty(x.Attributes["name"]?.Value) && szNodeList.Contains(x.Attributes["name"].Value))
.ToList()
// and, remove them from their parent
.ForEach(x => x.ParentNode.RemoveChild(x));
One of the problems here is terminology. You're not trying to remove attributes, as I understand it - you're trying to remove whole elements, based on the value of the name attribute.
If you can possibly use LINQ to XML for this, I would do so. It generally makes working with XML a lot easier. Here's a complete program to do what you want:
using System;
using System.Linq;
using System.Xml.Linq;
class Test
{
static void Main()
{
var namesToRemove = new[]
{
"titleTextColor",
"isLightTheme",
"showText"
};
XDocument doc = XDocument.Load("test.xml");
// For all the elements directly under the document root...
doc.Root.Elements()
// Where the array contains the value of the "name" attribute...
.Where(x => namesToRemove.Contains((string) x.Attribute("name")))
// Remove them from the document
.Remove();
doc.Save("output.xml");
}
}
Output:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="cropImageStyle" format="reference" />
<attr name="drawerArrowStyle" format="reference" />
<attr name="height" format="dimension" />
<attr name="title" format="string" />
<attr name="navigationMode">
<enum name="listMode" value="1" />
<enum name="normal" value="0" />
<enum name="tabMode" value="2" />
</attr>
</resources>
I need to update StoreGeneratedPattern to "None" in edmx file programmaticaly for Property nodes, based on some criteara like Name attribute contains "Code" value, what will be XPath to select such Property elements?
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="3.0" xmlns:edmx="http://schemas.microsoft.com/ado/2009/11/edmx">
<!-- EF Runtime content -->
<edmx:Runtime>
<!-- SSDL content -->
<edmx:StorageModels>
<Schema Namespace="Model.Store" Provider="System.Data.CData.DynamicsCRM" ProviderManifestToken="DynamicsCRM" Alias="Self" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns:customannotation="http://schemas.microsoft.com/ado/2013/11/edm/customannotation" xmlns="http://schemas.microsoft.com/ado/2009/11/edm/ssdl">
<EntityType Name="Account">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="varchar" StoreGeneratedPattern="Identity" Nullable="false" />
<Property Name="AccountCategoryCode" Type="varchar" StoreGeneratedPattern="Computed" />
<Property Name="AccountClassificationCode" Type="varchar" StoreGeneratedPattern="Computed" />
<Property Name="AccountNumber" Type="varchar" />
Try xml linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement root = doc.Root;
XElement entityType = root.Descendants().Where(x => x.Name.LocalName == "EntityType").FirstOrDefault();
XNamespace ns = entityType.GetDefaultNamespace();
XElement toChange = entityType.Elements(ns + "Property").Where(x => ((string)x.Attribute("Name")).Contains("Code")).FirstOrDefault();
toChange.SetAttributeValue("StoreGeneratedPattern", "None");
}
}
}
I have the following XML:
<funds>
<fund name="A" ITEM0="7%" ITEM1="8%" ITEM2="9%" ITEM3="10%" ITEM4="11%" ITEM5="" />
<fund name="B" ITEM0="11%" ITEM1="11%" ITEM2="13%" ITEM3="14%" ITEM4="16%" ITEM5="" />
<fund name="C" ITEM0="" ITEM1="" ITEM2="" ITEM3="" ITEM4="" ITEM5="" />
<fund name="D" ITEM0="7%" ITEM1="8%" ITEM2="9%" ITEM3="10%" ITEM4="11%" ITEM5="" />
<fund name="E" ITEM0="2%" ITEM1="3%" ITEM2="3%" ITEM3="5%" ITEM4="5%" ITEM5="" />
<fund name="F" ITEM0="" ITEM1="" ITEM2="" ITEM3="" ITEM4="" ITEM5="" />
<fund name="G" ITEM0="3%" ITEM1="3%" ITEM2="3%" ITEM3="5%" ITEM4="5%" ITEM5="" />
</funds>
<ToAppend>
<append name="A" ITEM="10" />
<append name="B" ITEM="15" />
<append name="C" ITEM="20" />
<append name="D" ITEM="20" />
<append name="E" ITEM="15" />
<append name="F" ITEM="10" />
<append name="G" ITEM="10" />
</ToAppend>
How can I loop through all of the attributes in //ToAppend/append and if 'name' is a match in //funds/fund add the attribute ITEM from //ToAppend/append to //funds/fund ?
I'm trying to append the matching items to the first list but I'm not having too much luck. Trying to get this working via C# under the 2.0 framework.
Thanks in advance!
edit:
XmlNode xmlNodeInner = root.SelectSingleNode("//ToAppend/append");
XmlNode ToBeUpdated = root.SelectSingleNode("//funds/fund");
foreach (XmlElement element in ToBeUpdated)
{
Console.WriteLine(element.InnerXml);
//Match the 'name' from xmlNodeInner to the 'name' of ToBeUpdated
//if{magic occurs here and they match}
{
element.SetAttribute("ITEM6", "value from xmlNodeInner");
}
}
I just dont know how to do the comparison inquiry to determine if A=A, or even exists, since there is no guarentee on that.
Hopefully it would come out something like:
<fund name="G" ITEM0="3%" ITEM1="3%" ITEM2="3%" ITEM3="5%" ITEM4="5%" ITEM5="" ITEM6="10"/>
This seems to work:
namespace ConsoleApplication1
{
using System;
using System.Xml;
class Program
{
static void Main( string[] args )
{
const string xml = #"
<root>
<funds>
<fund name='A' ITEM0='7%' ITEM1='8%' ITEM2='9%' ITEM3='10%' ITEM4='11%' ITEM5='' />
<fund name='B' ITEM0='11%' ITEM1='11%' ITEM2='13%' ITEM3='14%' ITEM4='16%' ITEM5='' />
<fund name='C' ITEM0='' ITEM1='' ITEM2='' ITEM3='' ITEM4='' ITEM5='' />
<fund name='D' ITEM0='7%' ITEM1='8%' ITEM2='9%' ITEM3='10%' ITEM4='11%' ITEM5='' />
<fund name='E' ITEM0='2%' ITEM1='3%' ITEM2='3%' ITEM3='5%' ITEM4='5%' ITEM5='' />
<fund name='F' ITEM0='' ITEM1='' ITEM2='' ITEM3='' ITEM4='' ITEM5='' />
<fund name='G' ITEM0='3%' ITEM1='3%' ITEM2='3%' ITEM3='5%' ITEM4='5%' ITEM5='' />
</funds>
<ToAppend>
<append name='A' ITEM='10' />
<append name='B' ITEM='15' />
<append name='C' ITEM='20' />
<append name='D' ITEM='20' />
<append name='E' ITEM='15' />
<append name='F' ITEM='10' />
<append name='G' ITEM='10' />
</ToAppend>
</root>
";
// XPath that finds all "funds/fund" nodes that have a "name" attribute with the value "{0}".
const string xpathTarget = #"//funds/fund[#name='{0}']";
// XPath that finds all "ToAppend/append" nodes that have a "name" and "ITEM" attribute.
const string xpathSourceNodes = #"//ToAppend/append[#name and #ITEM]";
var doc = new XmlDocument();
doc.LoadXml( xml );
foreach ( XmlNode sourceNode in doc.SelectNodes( xpathSourceNodes ) )
{
string name = sourceNode.Attributes[ "name" ].Value;
string item = sourceNode.Attributes[ "ITEM" ].Value;
XmlNode targetNode = doc.SelectSingleNode( String.Format( xpathTarget, name ) );
if ( null != targetNode )
{
XmlAttribute newAttribute = doc.CreateAttribute( "ITEM6" );
newAttribute.Value = item;
targetNode.Attributes.Append( newAttribute );
}
}
}
}
}
There is next xml file:
<element Name="root">
<SubFields>
<element Name="subroot">
<SubFields>
<element1 Name="element1" customatt1 = "12313" customatt2 = "asdfasfadsfasd">
<subelement Name="subelement" />
</element1>
<element1 Name="element11" customatt1 = "12313" customatt2 = "asdfasfadsfasd">
<subelement Name="subelement" />
</element1>
<element1 Name="element111" customatt1 = "12313" customatt2 = "asdfasfadsfasd">
<subelement Name="subelement" />
</element1>
<element2 Name="element2" path = "asdfdsf" widget="asdasdasd">
<subelement Name="subelement" />
</element2>
<element2 Name="element22" path = "asdfdsf" widget="asdasdasd">
<subelement Name="subelement" />
</element2>
<element2 Name="element222" path = "asdfdsf" widget="asdasdasd">
<subelement Name="subelement" />
</element2>
</SubFields>
</element>
</SubFields>
</element>
I mapped the array of elements as [XmlArray("SubFields")] where SubFields is root of arrays and Question:
How to map differences types of elements in object ?
And I can have a lot of subroot -s elements.
I used xsd.exe to do it.
Use the XMLSerializer class. Near the bottom theres a section about using Property attributes to map the object to the xml elements