I have a string [XML], I want to find tags that contain a word and replace that tag with another name.
I have tried using this, with no success:
var regex = new Regex(#"\bKeyValueOfstringOutcome\b", RegexOptions.IgnoreCase);
string result = regex.Replace(xml.Document.ToString(), "");
This is my XML:
<Response>
<Outcome>
<KeyValueOfstringOutcomeTest1>
<Key>Icon</Key>
<Value>
<DataType>System.String</DataType>
<Field>Icon</Field>
<Value>O</Value>
</Value>
</KeyValueOfstringOutcomeTest1>
<KeyValueOfstringOutcomeTest2>
<Key>IconDescription</Key>
<Value>
<DataType>System.String</DataType>
<Field>IconDescription</Field>
<Value>Old</Value>
</Value>
</KeyValueOfstringOutcomeTest2>
<KeyValueOfstringOutcomeTest3>
<Key>IconLongDescription</Key>
<Value>
<DataType>System.String</DataType>
<Field>IconLongDescription</Field>
<Value>Older</Value>
</Value>
</KeyValueOfstringOutcomeTest3>
</Outcome>
</Response>
I need to find the nodes that contain KeyValueOfstringOutcome, and replace KeyValueOfstringOutcomeTest1, KeyValueOfstringOutcomeTest2, KeyValueOfstringOutcomeTest3 with KeyValueOfstringOutcome.
If I understood you correctly that you want to get rid of Test1, Test2 and so on.
I would suggest to use this Replace method.
string replacedXML = Regex.Replace(xml.Document.ToString(),
#"KeyValueOfstringOutcomeTest\d+",
"KeyValueOfstringOutcome");
\d+ will match 1 or more occurences of a digit behind your keyword
I wouldn´t use REGEX to match and change the name of the nodes.
If you ever update your file or namings it´s going to crash or change some other node which contains the buzzword, too.
So I would create a loop through each childnode at the lvl/hierarchy you want to change and then grap it and rename it.
foreach (XmlNode node in doc.ChildNodes)
{
// [2] should be the the deepth of childenotes named `KeyValueOfstringOutcomeTestX`
node.ChildNode[2].Name = "YOUR NEW NAME TO THIS NODE";
}
doc.Save(); // Change renamed nodes to the document.
Use xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication107
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<XElement> keyValueOfstringOutcomeTests = doc.Descendants().Where(x => x.Name.LocalName.StartsWith("KeyValueOfstringOutcomeTest")).ToList();
foreach (XElement keyValueOfstringOutcomeTest in keyValueOfstringOutcomeTests)
{
keyValueOfstringOutcomeTest.ReplaceWith(new XElement("KeyValueOfstringOutcomeTest", keyValueOfstringOutcomeTest.Elements()));
}
}
}
}
Try this if it help.
string result = Regex.Replace(xml.Document.ToString(), "[a-zA-Z0-9]*KeyValueOfstringOutcome[a-zA-Z0-9]*","KeyValueOfstringOutcome");
This is what I've done to accomplish this.
var match = Regex.Match(xml.Document.ToString(), #"KeyValueOfstringOutcome.*>");
if (match.Success)
{
var replacedText = Regex.Replace(xml.Document.ToString(), #"KeyValueOfstringOutcome.*>", "KeyValueOfstringOutcome>");
}
Related
I don't have much experience with XML files but I'm trying to append a tutorial I found online to suite my needs and I'm not getting the results I would expect.
https://support.microsoft.com/en-us/help/307548/how-to-read-xml-from-a-file-by-using-visual-c
I've searched around but everything I've found doesn't make sense to me.
My XML looks like this for the most part:
<US>
<!-- Kentucky Start -->
<State>Kentucky KY
<City>Newport
<Street>Pavilion Parkway<Number>130<PostalCode>41071</PostalCode></Number></Street>
</City>
<City>Corbin
<Street>Highway 90<Number>7351<PostalCode>40701</PostalCode></Number></Street>
</City>
</State>
</US>
I'm trying to populate a listbox with the value of each state but my code either returns white space or just the text within the XML tag
e.g.
State..
State..
repeated for each element.
while (reader.Read()) {
switch (reader.NodeType) {
case XmlNodeType.Element: // The node is an element.
// Skip over root element
if (reader.Name.Equals("US")) {
reader.MoveToNextAttribute();
}
else {
if(reader.Name.Equals("State")) {
lbState.Items.Add(reader.Name);
lbState.Items.Add(reader.Value);
}
}
break;
reader.Name returns "State"
reader.Value returns "Whitespace"
I don't understand why reader.Value does not return Kentucky KY...
I've seen other examples that use string builder, is this a bad approach?
Use reader.ReadString() instead of reader.Value
Try xml linq :
sing System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication2
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("State").Select(x => new {
cities = x.Elements("City").Select(y => new {
state = (string)x,
city = (string)y,
streets = y.Elements("Street").Select(z => (string)z).ToList()
}).ToList()
}).SelectMany(x => x.cities).ToList();
}
}
}
You can use XmDocument (see: https://msdn.microsoft.com/en-us/library/system.xml.xmldocument(v=vs.110).aspx) and then use an xpath expression to get the right elements from your document:
Also it is better to encapsulate the name of the state (that is, if you own the xml document) like this:
<name>Kentucy KY</name>
So you can do the following:
var items = new List<string>();
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml("yourxml");
var xmlNodes = xmlDocument.SelectNodes("//State/Name");
foreach (XmlNode node in xmlNodes)
{
items.Add(xmlNode.Value);
}
I have an XML-file like this:
<Inventory>
<Item>
<Name>Super Mario Bros</Name>
<Count>14</Count>
<Price>29,99</Price>
<Comment><No Comments on this Product></Comment>
<Artist>N/A</Artist>
<Publisher>Nintendo</Publisher>
<Genre>Video Games</Genre>
<Year>1985</Year>
<ProductID>001</ProductID>
</Item>
<Item>
<Name>The Legend of Zelda</Name>
<Count>12</Count>
<Price>34,99</Price>
<Comment><No Comments on this Product></Comment>
<Artist>N/A</Artist>
<Publisher>Nintendo</Publisher>
<Genre>Video Games</Genre>
<Year>1986</Year>
<ProductID>002</ProductID>
</Item>
<Item>
<Name>Street Fighter</Name>
<Count>82</Count>
<Price>19,99</Price>
<Comment><No Comments on this Product></Comment>
<Artist>N/A</Artist>
<Publisher>Nintendo</Publisher>
<Genre>Video Games</Genre>
<Year>1987</Year>
<ProductID>003</ProductID>
</Item>
</Inventory>
(There are more Items, but they are all the same, except for the values.)
Now I want to iterate through each Item and extract every value from each node. Here's what I've tried so far:
var xDocument = XDocument.Load(FilePath_CSVToXML);
string xml = xDocument.ToString();
StringBuilder sb = new StringBuilder();
foreach (XElement xe in xDocument.Descendants("Inventory")) {
sb.Append(xe);
}
Console.WriteLine(sb.ToString());
Console.ReadLine();
The code above properly displays the XML-file, but it keeps the Nodes. (Name, Count, Price, etc.) I only want the values.
You need to use the Value property, i.e. sb.Append(xe.Value).
Try code below. The innertag of Comment doesn't need angle brackets so remove.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication49
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("Item").Select(x => new {
name = (string)x.Element("Name"),
count = (int)x.Element("Count"),
price = (decimal)x.Element("Price"),
comment = (string)x.Element("Comment"),
artist = (string)x.Element("Artist"),
publisher = (string)x.Element("Publisher"),
genre = (string)x.Element("Genre"),
year = (int)x.Element("Year"),
productID = (string)x.Element("ProductID")
}).ToList();
}
}
}
List<string> list = new List<string>();
foreach (XPathNavigator node in nav.Select("configuration/company/work/worktime"))
{
string day = getAttribute(node, "day");
string time = getAttribute(node, "time");
string worktype = ?? // how to get worktype attribute valuefrom parent node
list.Add(day,time,worktype); // add to list
}
</configuration>
<company>
<work worktype="homeWork">
<worktime day="30" time="10:28"></worktime>
<worktime day="25" time="10:50"></worktime>
</work>
<work worktype="officeWork">
<worktime day="12" time="09:28"></worktime>
<worktime day="15" time="12:28"></worktime>
</work>
</company>
</configuration>
need output as :
list[0] = homeWork,30,10:28
list[1] = homeWork,25,10:50
list[2] = officeWork,12,09:28
list[3] = officeWork,15,12:28
I am trying to get the list from XML but failed to get output like given above (using xpath navigator, how can I access parent node to get worktype attribute, and other remaining inner node attribute?
I'd suggest using LINQ to XML over XPath, but if you must use XPathNavigator then you need to iterate each work element followed by each of its worktime child elements. This way you can use the worktype from the parent context:
foreach (XPathNavigator work in nav.Select("configuration/company/work"))
{
var workType = work.GetAttribute("worktype", string.Empty);
foreach (XPathNavigator worktime in work.Select("worktime"))
{
var day = worktime.GetAttribute("day", string.Empty);
var time = worktime.GetAttribute("time", string.Empty);
list.Add($"{workType}, {day}, {time}");
}
}
See this fiddle for a working demo.
Use a nested loop. Initially retrieve the work nodes with configuration/company/work. Retrieve the worktype attribute and store in a variable. Then loop through the child worktype nodes and add a string to the list for each one
Use Net Library enhanced xml (linq xml)
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);
var results = doc.Descendants("work").Select(x => new {
worktype = (string)x.Attribute("worktype"),
worktime = x.Elements("worktime").Select(y => new {
day = (int)y.Attribute("day"),
time = (DateTime)y.Attribute("time")
}).ToList()
}).ToList();
}
}
}
I have an XmlNode, and its OuterXml is the next code I have posted. I need to know how to get The name and the age for each Campaign.
XmlNode Response = client.GetNamesAndAges(xmlRequest);
<Example>
<FromDate>12-05-2016</FromDate>
<ToDate>25-05-2016</ToDate>
<Campaigns>
<Campaign>
<Name>A</Name>
<age>2</age>
</Campaign>
<Campaign>
<Name>B</Name>
<age>1</age>
</Campaign>
</Campaigns>
<Status></Status>
</Example>
You can use XPath via SelectNodes() to get specific nodes/elements i.e Campaign elements in this case, and then print Name and age value from each Campaign :
var campaignList = Response.SelectNodes("Campaigns/Campaign");
foreach(XmlNode campaign in campaignList)
{
Console.WriteLine(campaign["Name"].InnerText);
Console.WriteLine(campaign["age"].InnerText);
}
BTW, Name and age are elements. Attributes in XML is used to reference something else i.e bar is the name of the attribute which value is baz in the following XML element <foo bar="baz"/>.
Use 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);
var campaign = doc.Descendants("Campaign").Select(x => new
{
name = (string)x.Element("Name"),
age = (int)x.Element("age")
}).ToList();
}
}
}
My XML is structured like this:
<Database>
<Member>
<Name>PersonA</Name>
<Rank>RankIWant</Rank>
</Member>
<Member>
<Name>PersonB</Name>
<Rank>RankIDontWant</Rank>
</Member>
</Database>
I have the <Name> value of PersonA, and I want "RankIWant", but I'm not sure how to get it with the information that I have. What do I do?
You can use an XPath query. For your example:
//Member[Name = "PersonA"]/Rank
Fiddle: https://dotnetfiddle.net/udobyd
That XPath query means, select all (the // means all, independent of the position) Rank nodes who have a parent Member node which happens to have a Name descendant with the value PersonA.
You could also use Linq to Xml if you are used to Linq. In this case, the equivalent code would be:
var nodes = XElement.Parse(xml).Descendants("Rank")
.Where(x => x.Parent.Descendants("Name").Any(y => y.Value == "PersonA"));
Fiddle here: https://dotnetfiddle.net/bOVOy7
For your specific query, I'd prefer the XPath version, but your choice.
Use 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
{
static void Main(string[] args)
{
string xml =
"<Database>" +
"<Member>" +
"<Name>PersonA</Name>" +
"<Rank>RankIWant</Rank>" +
"</Member>" +
"<Member>" +
"<Name>PersonB</Name>" +
"<Rank>RankIDontWant</Rank>" +
"</Member>" +
"</Database>";
XElement database = XElement.Parse(xml);
XElement query = database.Elements("Member")
.Where(x => x.Element("Rank").Value == "RankIWant").FirstOrDefault();
}
}
}
Here is another method.
private static void Main()
{
string myRankValue = "";
var xml = #"<Database>
<Member>
<Name>PersonA</Name>
<Rank>RankIWant</Rank>
</Member>
<Member>
<Name>PersonB</Name>
<Rank>RankIDontWant</Rank>
</Member>
</Database>";
var xDoc = XDocument.Parse(xml);
var firstMember =
xDoc.Descendants("Member")
.Where(d => d.Descendants("Name").First().Value == "PersonA")
.Descendants("Rank")
.FirstOrDefault();
if (firstMember != null)
{
myRankValue = firstMember.Value;
}
Console.WriteLine(myRankValue);
}
}