Writing data to existing Xml - c#

I am developing a universal windows app on windows 10 with Visual Studio 2015 and have a pretty large Xml structured like this:
<header id = "1">
<title>
some text
</title>
<question>
a question
</question>
<user_input>
<input1>
</input1>
<input2>
</input2>
</user_input>
</header>
<header id = "2">
<title>
some text
</title>
<question>
a question
</question>
<user_input>
<input1>
</input1>
<input2>
</input2>
</user_input>
</header>
...
This is repeating many times. There are parts that should never be changed (e.g. title, question). Now i want to write new elements into "ui", so it can be read again and shows the new content in texbox.
I use a FileStream and XmlDocument and XmlNodeList to read the Xml and show the content on textblocks:
path = "test.xml";
FileStream stream = new Filestream(path, FileMode.Open, FileAcces.Read);
XmlDocument xdoc = new XmlDocument();
xdoc.Load(reader);
XmlNodeList node = xdoc.GetElementsByTagName("header");
textblock1.Text = node[0].Attributes["id"].Value;
textblock2.Text = node[i].ChildNode[1].InnerText;
....
I tried this to write into the Xml:
XDocument xdoc = XDocument.Load(path);
XElement ele = xdoc.Element("header");
ele.Add(new XElement("user_input",
new XElement("input1", newtext)));
xdoc.Save(path); <---- at this point there is an error
"Argument 1: cannot convert from 'string' to 'System.IO.Stream'"
My question is: how can i write the user input (some string) to the place I want it to be? The first input shall be written into header with id = 1 into user_input, the second into header id = "2" and so on. I already tried to load the xml with XDocument and write a new element with XElement, but it work at all.Is there something wrong with my xml? Or is it the function? Thank you in advance.

Firstly, the xml file cannot contain same roots, here you have two headers nodes but don't see a root node. So I add a root node for testing your xml file as follows
<?xml version="1.0" encoding="utf-8"?>
<Topics>
<header id = "1">
...
</header>
</Topics>
Secondly, this error
"Argument 1: cannot convert from 'string' to 'System.IO.Stream'"
xdoc.save(string) is not available in uwp, details you can see the version information of XDocument.Save method.
Thirdly, for this question
how can i write the user input (some string) to the place I want it to be?
we can insert value to special element by xpath or GetElementsByTagName method. In uwp, I recommend you use Windows.Data.Xml.Dom namespace instead of System.xml.Ling.
Here I wrote a demo for insert value to special place . And upload the demo to GitHub, you can download CXml for testing.
Mainly Code
private async void BtnXmlWrite_Click(object sender, RoutedEventArgs e)
{
String input1value = TxtInput.Text;
if (null != input1value && "" != input1value)
{
var value = doc.CreateTextNode(input1value);
//find input1 tag in header where id=1
var xpath = "//header[#id='1']/user_input/input1";
var input1nodes = doc.SelectNodes(xpath);
for (uint index = 0; index < input1nodes.Length; index++)
{
input1nodes.Item(index).AppendChild(value);
}
RichEditBoxSetMsg(ShowXMLResult, doc.GetXml(), true);
}
else
{
await new Windows.UI.Popups.MessageDialog("Please type in content in the box firstly.").ShowAsync();
}
}
More details you can reference XML dom Sample, XML and XPath.

Related

How to get the xml node value into a string

How to get the xml node value in a string.
i am getting This error
Data at the root level is invalid. Line 1, position 1.
error shown in this line
xmldoc.LoadXml(xmlFile);
my xml
<?xml version="1.0" encoding="utf-8" ?>
<UOM>
<!-- The selected currency used will be stored here for Code reference" -->
<ActiveCurrencyType>
<ActiveCurrency>U.S.Dollar</ActiveCurrency>
<ActiveCode>USD</ActiveCode>
<ActiveSymbol>$</ActiveSymbol>
</ActiveCurrencyType>
<!-- The selected Dimension used will be stored here for Code reference -->
<ActiveDimension>
<ActiveDimensionUOM>Inches</ActiveDimensionUOM>
<ActiveDimensionSymbol>.in</ActiveDimensionSymbol>
</ActiveDimension>
<!-- The selected weight used will be stored here for Code reference -->
<ActiveWeight>
<ActiveWeightUOM>Pounds</ActiveWeightUOM>
<ActiveWeightSymbol>lb</ActiveWeightSymbol>
</ActiveWeight>
</UOM>
C# code
string xmlFile = Server.MapPath("~/HCConfig/HCUOM.xml");
XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(xmlFile);
XmlNodeList nodeList = xmldoc.GetElementsByTagName("ActiveDimensionSymbol");
string ActiveDimensionSymbol = string.Empty;
foreach (XmlNode node in nodeList)
{
ActiveDimensionSymbol = node.InnerText;
}
How can I achieve this?
You're using the wrong overload, LoadXml doesn't do what you think it does.
Use xmldoc.Load(xmFile); because that method takes an file path as input. LoadXml expects an string with xml in it.
The exception is an indicator of that mistake. What is processed is not XML, and a filepath isn't that.
After this changes the string ActiveDimensionSymbol contains .in if I run this locally.
If you want to use LoadXml you should first read your whole file in a string, for example like so:
xmldoc.LoadXml(File.ReadAllText(xmlFile));
but is really only overhead to call File.ReadAllText if there is an method that accepts a file.
You can use the Descendants() method to get all XElements by certain name, found in the System.Xml.Linq namespace.
XDocument doc = XDocument.Load("XMLFile1.xml");
string[] allActiveWeightUOMs = doc.Descendants("ActiveWeightUOM").Select(o => o.Value).ToArray();
// allActiveWeightUOMs : "Pounds" ...
As can seen here link the method that you are using to load the XML excepts xml by string not xml file. You can use XmlDocument.Load instead of XmlDocument.LoadXml
Try this code its works just fine with this xml
string xmlFile = Server.MapPath("~/HCConfig/HCUOM.xml");
XDocument doc = XDocument.Load(xmlFile );
var nodeList = doc.Descendants("ActiveDimensionSymbol");
string ActiveDimensionSymbol = string.Empty;
foreach (var node in nodeList)
{
ActiveDimensionSymbol = node.Value;
}

XElement parses my XML file as one huge element, how do I fix?

So I am trying to parse through some XML which is being returned from a REST API call. The XML looks like this (with many more <link>'s of course):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response status="Ok">
<links>
<link id="79380" hint="Some Text" linkDescription="This is the GET url for this Customer." link="/customer/79380" httpMethod="GET"/>
</links>
</response>
I am loading the XML file using the following code:
Stream resStream = response.GetResponseStream();
StreamReader reader = new StreamReader(resStream);
XElement doc = XElement.Load(reader);
I then loop through the elements like so:
IEnumerable<XElement> List =
from el in doc.Descendants("links") select el;
foreach (XElement e in List)
{
test += e.ToString();
}
It only loops through once and test is just a string that contains the entire XML file. My goal is to get the value of the "id" attribute from each element and place them in a list.
I have tried various things and I can't seem to get anything back but one huge string.
Try this:
var idList = doc.Descendants("link").Select(x => (int)x.Attribute("id"));

Deleting node from string with xml structure

I have an string parameter with xml content in it. Basically the string have an XML inside.
string S = funcThatReturnsXML (parameters);
S have the next text:
<?xml version="1.0" encoding="utf-8" ?>
<tagA>
<tagB>
<tagBB>
..
.
.
</tagBB>
.
.
</tagB>
<tagC>
..
..
.
</tagC>
</tagA>
The funcThatReturnsXML (parameters) creates an XmlDocument object but the return it as a string, I cant change this function, to much stuff works with it.
Tried to create XmlDocument objetc but the SelectSingleNode return null.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(S);
XmlNode root = xmlDoc.SelectSingleNode("tagB");
How can I delete from string S (not XML Object) specific node, for example <tagB>
EDIT: this is the XML I tested with:
<?xml version="1.0" ?>
- <Request xmlns:xsi="http://www.mysite.com" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <info xmlns="http://www.mysite.com">
<RequestTR>54</RequestTR>
<time>2013-12-22</time>
</info>
- <Parameters xmlns="http://www.mysite.com">
<id>3</id>
<name>2</name>
</Parameters>
<title>Request</title>
</Request>
Try this:
string S = funcThatReturnsXML(parameters);
var doc = XDocument.Parse(S);
var nodeToRemove = doc.Descendants("tagB");
nodeToRemove.Remove();
That will remove all nodes named "tagB" from string S which contains xml.
UPDATE 1:
Sorry, i missed to include one more line:
S = doc.ToString();
My first code above removed "tagB" from doc but didnt save it back to S variable.
UPDATE 2:
I tested with following xml which contain attribute:
<tagA attribute="value">
<tagB>
<tagBB>
</tagBB>
</tagB>
<tagC></tagC>
</tagA>
and the output of Console.WriteLine(S):
<tagA attribute="value">
<tagC></tagC>
</tagA>
UPDATE 3:
Given your updated xml format, I know why my previous code didn't work for you. That was because your xml have namespace (xmlns) declared. The solution is to use LocalName when searching for the node to be removed, that will search for node name while ignoring its namespace. The follwoing example shows how to remove all "info" node:
var doc = XDocument.Parse(S);
var nodeToRemove = doc.Descendants().Where(o => o.Name.LocalName == "info");
nodeToRemove.Remove();
S = doc.ToString();
If you can determine the particular outer element to remove from the returned XML, you could use LINQ to XML:
var returnedXml = funcThatReturnsXML(parameters);
var xmlElementToRemove = funcThatReturnsOuterElement(returnedXml);
var xelement = XElement.Load("XmlDoc.txt");
xelement.Elements().Where(e => e.Name == xmlElementToRemove).Remove();
For example:
using System.Linq;
using System.Xml.Linq;
class Program
{
static void Main(string[] args)
{
// pretend this is the funThatReturnsXML return value
var returnedXml = "<tagB><tagBB></tagBB></tagB>";
// get the outer XML element name
var xmlElementToRemove = GetOuterXmlElement(returnedXml);
// load XML from where ever
var xelement = XElement.Load("XmlDoc.txt");
// remove the outer element and all subsequent elements
xelement.Elements().Where(e => e.Name == xmlElementToRemove).Remove();
}
static string GetOuterXmlElement(string xml)
{
var index = xml.IndexOf('>');
return xml.Substring(1, index - 1);
}
}
Note that the above is a "greedy" removal method, if there is more than once element with the name returned via the GetOuterXmlElemet method they will all be removed. If you want a specific instance to be removed then you will require something more sophisticated.
Building on your edit:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(S);
var nodeA = xmlDoc.SelectSingleNode("/tagA");
var nodeB = nodeA.SelectSingleNode("tagB");
nodeA.RemoveChild(nodeB);
To remove (possibly) multiple tagB nodes in unknown positions, you may try:
var bees = xmlDoc.SelectNodes("//tagB");
foreach (XmlNode bee in bees) {
var parent = bee.ParentNode;
parent.RemoveChild(bee);
}

Reading XML only contents from a Log file

I have a log file which stores the data in XML format. I want to read this data but the problem that I am getting is that log file is not well structured XML file. It contains some additional data like :
03/22/2013 : 13:23:32 <?xml version="1.0" encoding="UTF-8"?>
<element1>
...
...
...
</element1>
As you will notice 03/22/2013 : 13:23:32 is not allowing me to read data and throwing exception saying "Data at the root level is invalid"
I am using following method to read XML
XmlDocument doc = new XmlDocument();
doc.Load("file.log");
string xmlcontents = doc.InnerXml;
label1.Text = xmlcontents;
Please guide me through the solution as this is a rare case for me. I tried googling for a solution but couldnt succeed
Thanks
A quick hack would be to parse the log file to extract only the text found between the root xml tags, in your case, what is found between < element1> and < /element1>.
You can search for the tag < ?xml, and what comes after ?> is your root tag, and go with that. Depending on how predictable of clearly structured the log file is, you can formulate better ways of doing this, but if nothing else works, you can try this way.
var doc = new XmlDocument();
doc.LoadXml(string.Concat(File.ReadAllLines("file.log").Skip(1)));
Reorganize your XML, so the date become an Element or Attribute, if the file is too large
Maybe you should read whole file to array of lines (System.IO.File.ReadAllLines(string path)) and then join elements of array skipping first line and rest of lines which is not fragments of XML structure (assuming your sample is only part of input file).
You can skip first line
var onlyXml = (File.ReadAllLines("file.log")).Skip(1).SelectMany(l => l).ToArray();
var xmlContent = new String(onlyXml);
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlContent);
string xmlcontents = doc.InnerXml;
label1.Text = xmlcontents;
EDIT
You can get only xml between first '<' and last '>'
var text = File.ReadAllText("file.log");
var beginIndex = text.IndexOf('<');
var endIndex = text.LastIndexOf('>');
var onlyXml = text.Substring(beginIndex, endIndex - beginIndex + 1);

I am trying to read directory from xml file in c# and have problem

<?xml version="1.0" encoding="UTF-8"?>
<form:Documents xmlns:form="http://www.abbyy.com/FlexiCapture/Schemas/Export/FormData.xsd" xmlns:addData="http://www.abbyy.com/FlexiCapture/Schemas/Export/AdditionalFormData.xsd">
<_Document_Definition_1:_Document_Definition_1 addData:ImagePath="C:\POC\Export\Test.pdf" xmlns:_Document_Definition_1="http://www.abbyy.com/FlexiCapture/Schemas/Export/Document_Definition_1.xsd">
<_Page_1>
<_First_Name>John</_First_Name>
<_Last_Name>Doe</_Last_Name>
</_Page_1>
</_Document_Definition_1:_Document_Definition_1>
</form:Documents>
I have xml containing directory of pdf file which I would need to read.
I can read first name and last name from _Page_1 node but do not know how to read ImagePath.
Here is my code to read from _Page_1
XDocument xDoc = XDocument.Load("Test.xml");
var poc = from p in xDoc.Descendants("_Page_1")
select new
{
FirstName = p.Element("_First_Name").Value,
LastNumber = p.Element("_Last_Name").Value
};
// Execute the query
foreach (var customer in poc)
{
Console.WriteLine(customer.FirstName);
Console.WriteLine(customer.LastName);
}
//Pause the application
Console.ReadLine();
Thank you BrokenGlass, it's working.
I have one more question.
What if I have several iteration of _Document_Definition node, how do I read each iteration.
<?xml version="1.0" encoding="UTF-8"?>
<form:Documents xmlns:form="http://www.abbyy.com/FlexiCapture/Schemas/Export/FormData.xsd" xmlns:addData="http://www.abbyy.com/FlexiCapture/Schemas/Export/AdditionalFormData.xsd">
<_Document_Definition_1:_Document_Definition_1 addData:ImagePath="C:\POC\Export\Test.pdf" xmlns:_Document_Definition_1="http://www.abbyy.com/FlexiCapture/Schemas/Export/Document_Definition_1.xsd">
<_Page_1>
<_First_Name>John</_First_Name>
<_Last_Name>Doe</_Last_Name>
</_Page_1>
</_Document_Definition_1:_Document_Definition_1>
<_Document_Definition_1:_Document_Definition_1 addData:ImagePath="C:\POC\Export\Test2.pdf" xmlns:_Document_Definition_1="http://www.abbyy.com/FlexiCapture/Schemas/Export/Document_Definition_1.xsd">
<_Page_1>
<_First_Name>Jane</_First_Name>
<_Last_Name>Doe</_Last_Name>
</_Page_1>
</_Document_Definition_1:_Document_Definition_1>
</form:Documents>
You are missing the XML namespace references to access those attributes, this works:
XDocument doc = XDocument.Load(#"test.xml");
XNamespace _Document_Definition_1 = "http://www.abbyy.com/FlexiCapture/Schemas/Export/Document_Definition_1.xsd";
XNamespace addData = "http://www.abbyy.com/FlexiCapture/Schemas/Export/AdditionalFormData.xsd";
string impagePath = doc.Descendants(_Document_Definition_1 + "_Document_Definition_1")
.First()
.Attribute(addData + "ImagePath")
.Value;
It looks like Imagepath is an attribute not an element. Hence you are not able to read it. Check for the attributes in the xml file.

Categories