I have a XML file with the following structure:
<deftable>
<table>
<job jobname="">
<incond name="" />
<outcond name="" />
</job>
<job jobname="">
<incond name="" />
<outcond name="" />
<incond name="" />
<outcond name="" />
</job>
</table>
<table>
<job jobname="">
<incond name="" />
<outcond name="" />
</job>
<job jobname="">
<incond name="" />
<outcond name="" />
<incond name="" />
<outcond name="" />
</job>
</table>
</deftable>
inside the tag deftable i can have multiple tables.
in the table tag i can have multiple JOBs, and inside those I have multiple incond and outcond.
I'm trying to obtain the values for jobname, and also the value for the name attribute of incond and outcond.
I have tried several paths to accomplish this. The latest thing I tried was this, but I can't seem to get it to work.
XmlDocument doc = new XmlDocument();
doc.Load(file);
XmlNodeList nodes = doc.DocumentElement.SelectNodes("/deftable/table");
int i = 1;
foreach (XmlNode node in nodes)
{
Console.WriteLine(node["job"].GetAttribute("jobname") + " -- " + i);
i++;
XmlNodeList nodes2 = doc.DocumentElement.SelectNodes("/deftable/table/job");
foreach (XmlNode item in nodes2)
{
Console.WriteLine(item["incond"].GetAttribute("name"));
}
}
Any help is greatly appreciated.
I am not sure how much the following snippet is useful for you, but you can try with Linq to Xml for your case as shown below.
XDocument jobsRoot = XDocument.Load(#"C:\Jobs.xml");
var jobData= jobsRoot.Descendants()
.Where(it => it.Name.LocalName == "job")
.Select(job => new
{
JobName = job.Attribute("jobname").Value,
IncondName = job.Descendants().Where(it => it.Name.LocalName == "incond").Select(inc => inc.Attribute("name").Value),
OutcondName = job.Descendants().Where(it => it.Name.LocalName == "outcond").Select(inc => inc.Attribute("name").Value)
});
I think the linq to XML implementation isn't as great as XML to Dynamic
It's described on Deserialize XML To Object using Dynamic ( since you mentioned you tried several methods). Otherwhise, the answer above would probably do.
Related
i'm re posting my question in a simpler way.
i need to search for a specific node in a XML file, and once i see it, i need to create a new node and insert it after that. the problem is that there are 2 nodes with the same value. and i need to insert the new node twice after each instance. with the code below: it's inserting the new nodes twice but in the same place after the first instance only.
original XML:
<eventlist>
<event type="AUDIOPLAYER">
<properties>
<schedule startType="-ParentEnd1" />
<media mediaType="Audio" />
</properties>
</event>
<event type="AUDIOPLAYER">
<properties>
<schedule startType="-ParentEnd2" />
<media mediaType="Audio" />
</properties>
</event>
</eventlist>
intended XML:
<eventlist>
<event type="AUDIOPLAYER">
<properties>
<schedule startType="-ParentEnd1" />
<media mediaType="Audio" />
</properties>
</event>
<event type="VIZ" />
<event type="AUDIOPLAYER">
<properties>
<schedule startType="-ParentEnd2" />
<media mediaType="Audio" />
</properties>
</event>
<event type="VIZ" />
</eventlist>
but the current output is:
<eventlist>
<event type="AUDIOPLAYER">
<properties>
<schedule startType="-ParentEnd1" />
<media mediaType="Audio" />
</properties>
</event>
<event type="VIZ" />
<event type="VIZ" />
<event type="AUDIOPLAYER">
<properties>
<schedule startType="-ParentEnd2" />
<media mediaType="Audio" />
</properties>
</event>
</eventlist>
the code is below here:
XmlDocument xdoc = new XmlDocument();
xdoc.Load(#"C:\Users\namokhtar\Desktop\newxml\testxml.xml");
foreach (XmlNode node in xdoc.SelectNodes("/eventlist/event[#type='AUDIOPLAYER']"))
{
XmlNode srcNode = node.SelectSingleNode("/eventlist/event[#type='AUDIOPLAYER']");
XmlNode newElem = xdoc.CreateElement("event");
XmlAttribute newAttr = xdoc.CreateAttribute("type");
newAttr.Value = "VIZ";
newElem.Attributes.Append(newAttr);
srcNode.ParentNode.InsertAfter(newElem, srcNode);
}
xdoc.Save(#"C:\Users\namokhtar\Desktop\newxml\newxml1.xml");
please advise me...
I haven´t fully tested this, but I´m almost sure this should do the trick:
foreach (XmlNode node in xdoc.SelectNodes("/eventlist/event[#type='AUDIOPLAYER']"))
{
XmlNodeList srcNodes = node.SelectNodes("/eventlist/event[#type='AUDIOPLAYER']");
foreach (XmlNode srcNode in srcNodes)
{
XmlNode newElem = xdoc.CreateElement("event");
XmlAttribute newAttr = xdoc.CreateAttribute("type");
newAttr.Value = "VIZ";
newElem.Attributes.Append(newAttr);
srcNode.ParentNode.InsertAfter(newElem, srcNode);
}
}
The problem is you were selecting single node from matching expression, and you need to select all the nodes that match that and insert the new node after each of them.
Hope this helps!
Here is a solution using LINQ:
var xml = XDocument.Parse(File.ReadAllText(#"C:\Users\namokhtar\Desktop\newxml\testxml.xml"));
var elems = xml.Root.Elements()
.Where(e => e.Name == "event" && e.Attribute("type")?.Value == "AUDIOPLAYER");
foreach (var elem in elems)
{
elem.AddAfterSelf(new XElement("event", new XAttribute("type", "VIZ")));
}
xml.Save(#"C:\Users\namokhtar\Desktop\newxml\newxml1.xml");
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'm trying to select nodes using LINQ but I cannot understand why this simple query does not work.
My xml is like this:
<config>
<func_list current_app="GSCC" flag="0">
<func id="GSCC">
<BLOCKS>
<BLOCK id="1" type="ACTIONS">
<ITEMS>
<ITEM id="1" type="UINT32" size="1" value="1" />
<ITEM id="2" type="UINT32" size="1" value="5" />
<ITEM id="3" type="UINT32" size="1" value="0" />
</ITEMS>
</BLOCK>
</BLOCKS>
</func>
</func_list>
</config>
Now, I have an XElement (_funcNode) which points to 'func' node:
IEnumerable<XElement> xBlocks = from el in _funcNode.Descendants("BLOCK")
where el.Attribute("type").Value == "ACTIONS"
select el;
if (!xBlocks.Any()) return false;
Also the xBlocks.Any() throws an System.NullReferenceException exception.
Any idea?
I solved changing the query to:
IEnumerable<XElement> xBlocks = from el in _funcNode.Descendants("BLOCK")
where (string)el.Attribute("type") == "ACTIONS"
select el;
Regards.
You main problem is your Query does not add up to your xml.
IEnumerable<XElement> xBlocks = from el in _funcNode.Descendants("BLOCK")
What you want to use is
_funcNode.Descendants().Elements("BLOCK")
Try to do this
var doc = _funcNode.Descendants("BLOCK")
See what doc looks like.
I'm wondering whether there is a way using C# which enables me to return all the inner values within an XML file matching a given XPath query.
Let's suppose that we have the following Xml file named exampleWithFruits.xml:
<fruits>
<bananas>
<banana id="1" color="yellow" price="0.5" />
<banana id="2" color="yellow" price="0.4" />
<banana id="3" color="yellow" price="0.6" />
</bananas>
<apples>
<apple id="1" color="red" price="0.5" />
<apple id="2" color="red" price="0.4" />
<apple id="3" color="green" price="0.6" />
<apple id="4" color="yellow" price="0.4" />
</apples>
<oranges>
<orange id="1" color="orange" price="0.5" />
<orange id="2" color="orange" price="0.5" />
</oranges>
</fruits>
Something like following below:
string xmlFilePath = "exampleWithFruits.xml";
string xPathQuery = "//fruits/apples//#color"
string[] matchingValues = interestingFunction(xmlFilePath, xPathQuery);
//for instance we would get something like : matchingValues = {red, red, green, yellow}
To sum up, I would like to know how to create a function such as interestingFunction
Thx
One way to do this is to use System.Xml.XPath.Extensions.XPathEvaluate.
E.g.
string xmlFilePath = "exampleWithFruits.xml";
string xPathQuery = "//fruits/apples//#color";
var doc = XDocument.Load(xmlFilePath);
IEnumerable att = (IEnumerable)doc.XPathEvaluate(xPathQuery);
string[] matchingValues = att.Cast<XAttribute>().Select(x => x.Value).ToArray();
Or if you prefer XmlDocument:
var doc = new XmlDocument();
doc.Load(xmlFilePath);
string[] matchingValues = doc.SelectNodes(xPathQuery).Cast<XmlAttribute>().Select(x => x.Value).ToArray();