Edit and save XML adding extra [] characters - c#

I have a requirement to edit and save- precisely replace a text in an xml (within the c# project) with a value from arguments and save in temp location. The value is replaced and saved in location, but it adds some characters- [] and hence when i use the xml in another application as input, it is shown as incorrect xml! Even when i remove the extra character and save and rerun it shows the same error. However when i remove the extra character and paste the whole xml into a new file it works fine! I dont understand whats the issue. Have pasted my code below:
{
parameterFileName = "test";
tempPath = Path.GetTempPath() + parameterFileName + DateTime.Now.ToString("dd-MM-yyyy_hh-mm-ss") + ".xml";
XmlDocument xdoc = GetParameterXML(parameterFileName);
XmlNode root = xdoc.DocumentElement;
XmlNode node = xdoc.DocumentElement.SelectSingleNode(#"/root/inputParameters");
XmlNode childNode = node.ChildNodes[0];
if (childNode is XmlCDataSection)
{
XmlCDataSection cdataSection = childNode as XmlCDataSection;
if (cdataSection.Value.Contains("ID_VALUE"))
{
cdataSection.Value = cdataSection.Value.Replace("ID_VALUE", id);
}
}
xdoc.Save(tempPath);
}
public static XmlDocument GetParameterXML(string parameterFileName)
{
var sDllPath = AppDomain.CurrentDomain.BaseDirectory;
XmlDocument xDoc = new XmlDocument();
xDoc.Load(sDllPath + "\\Templates\\" + parameterFileName + ".xml");
return xDoc;
}

When you parse Xml Document by using XmlDocument with DTD then empty Internal Subset means Square Brackets [] is automatically inserted.
public static XmlDocument GetParameterXML(string parameterFileName)
{
var sDllPath = AppDomain.CurrentDomain.BaseDirectory;
XmlDocument xDoc = new XmlDocument();
xDoc.Load(sDllPath + "\\Templates\\" + parameterFileName + ".xml");
if (xDoc.DocumentType != null)
{
var name = xDoc.DocumentType.Name;
var publicId = xDoc.DocumentType.PublicId;
var systemId = xDoc.DocumentType.SystemId;
var parent = xDoc.DocumentType.ParentNode;
var documentTypeWithNullInternalSubset = xDoc.CreateDocumentType(name, publicId, systemId, null);
parent.ReplaceChild(documentTypeWithNullInternalSubset, xDoc.DocumentType);
}
return xDoc;
}
Does it matter?
No this does not matter. but its a well formed XML if your XML doesn't contain any internal subset then it represent as blank square brackets []. it means that your xml doesn't contain any internal subset.
While parsing xml with XDocument with no internal subset then XDocument append blank square brackets [] instead of display nothing in DOCTYPE.
What does an empty internal subset do?
The basic purpose of an internal entity is to get rid of typing same content (like the name of the organization) again and again. And instead, we can define an internal entity to contain the text and then only you need to use the entity where you want to insert the text. Because the entity is expanded by the parser, you can be assured that you'll get the same text in every location. The parser will also catch if you misspell an entity name.
You can read more about Internal Subset here

Related

Read xml file containing multiple tags with same attribute name and replace the value based on user's input

Below is my sample XML file stored in the server ;
<exam>
<name>Maths</name>
<percentage>100</percentage>
</exam>
<exam>
<name>Physics</name>
<percentage>70</percentage>
</exam>
<exam>
<name>Chemistry</name>
<percentage>70</percentage>
</exam>
I have another table as mentioned below
Name of Exam Percentage
Maths 50
Physics 60
Chemistry 70
What I need here is that I need to read this XML File and replace the percentage value in the XML file based on the table that I have. I have more than 75 tags for exam
I have used the below logic of hardcoding everything but I am not sure if my logic would be good
public static void Changepercentage()
{
try{
string xmlpercentage= Loaddefault.xmlpercentage;
string f = xml
List<string> lines = new List<string>();
// 2
// Use using StreamReader for disposing.
using (StreamReader r = new StreamReader(f, System.Text.Encoding.Default))
{
// 3
// Use while != null pattern for loop
string line;
while ((line = r.ReadLine()) != null)
{
if (System.Text.RegularExpressions.Regex.IsMatch(line, "<exam>Maths</exam>"))
{
lines.Add(#"" + line + "");
line = "<percentage>50</percentage>";
}
}
}
System.IO.File.WriteAllLines(xmlpercentage, lines.ToArray());
Logger.Instance.InfoLog("Successfully updated the percentage.xml file");
}
catch (Exception ex)
{
Logger.Instance.ErrorLog("Problem in updating the percentage.xml file :"+ ex.Message);
throw new Exception("Problem in updating the percentage.xml file");
}
}
You can use this documentation
//Make sure that the project references the System.Xml namespace.
//Use the Imports statement on the Xml namespace
using System.Xml;
//Create a new XmlDocument class, and use the Load method to load it.
XmlDocument myXmlDocument = new XmlDocument();
myXmlDocument.Load ("test.xml");
//Iterate through the children of the document element, and find all the "percentage" nodes. and update it.
foreach(XmlNode node1 in node.ChildNodes)
foreach (XmlNode node2 in node1.ChildNodes)
if (node2.Name == "percentage")
{
Decimal percentage = 60;//your new percentage
node2.InnerText = percentage;
}
//Use the Save method of the XmlDocument class to save the altered XML to a new file that is named test1.xml.
myXmlDocument.Save("test1.xml");
Iterate over all <exam> nodes of your XML, read the child node <name>. With the InnerText of name query the data base and put the data base result into the <percentage> node.
Something like this should do:
var doc = XDocument.Parse(yourXml);
foreach(XElement exam in doc.Descendants("exam"))
{
var examName = exam.Descendants("name").First().Value;
var newPercentage = GetPercentage(examName);
exam.Descendants("percentage").First().Value = newPercentage;
}

How to get innertext from xml node that has '\' along with other characters when fetched as string

Hi i am facing an issue extracting innertext from an xml script saved as varchar2 in my oracle database.
While fetching i am getting the xml in the following format:
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<SpecificAction xmlns=\"http://www.xyz.com/schemas/core/caut\">action to take</SpecificAction>"
the following xml is fetched using the code below:
instance.cautDescription = records.GetStringOrDefault("SPEC_ACTION");
the field SPEC_ACTION is of VARCHAR2(4000 BYTE) type.
due to the presence of \ character in the xml from database i am not able to extract the inner text withing the Tag. I tried the following code for fetching the inner text:
string s = instance.cautDescription;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(s);
XmlNode specificAction = xmlDoc.DocumentElement.SelectSingleNode("/SpecificAction");
string specific = specificAction.InnerText;
xmlString = specificAction.InnerText;
return xmlString;
My code is returning null can this be avoided if so how , any support would be really helpful.
This is classic problem when working with XML having default namespace. In XML, when you have default namespace (namespace without prefix, like xmlns="...."), all elements without prefix considered in default namespace. But in XPath, all elements without prefix considered has no namespace. To bridge this different paradigm, you need to declare a prefix that point to default namespace URI and use it in XPath :
var nsManager = new XmlNamespaceManager(xmlDoc.NameTable);
nsManager.AddNamespace("ns", xmlDoc.DocumentElement.NamespaceURI);
XmlNode specificAction = xmlDoc.DocumentElement.SelectSingleNode("/ns:SpecificAction", nsManager);
string specific = specificAction.InnerText;
Since the "SpecificAction" node has a namespace, you will need to supply a namespace manager.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(s);
XmlNamespaceManager ns = new XmlNamespaceManager(xmlDoc.NameTable);
ns.AddNamespace("xyz", "http://www.xyz.com/schemas/core/caut");
XmlNode specificAction = xmlDoc.SelectSingleNode("/xyz:SpecificAction", ns);
return specificAction.InnerText;

Pulling string from xml

The xml is coming from a url and all I need is the pull the string "N0014E1" from it. I am not sure why this code is not working. I put a try block around it and I get a "Data root level is invalid"
xml:
<obj is="c2g:Network " xsi:schemaLocation="http://obix.org/ns/schema/1.0/obi/xsd" href="http://192.168.2.230/obix/config/">
<ref name="N0014E1" is="c2g:LOCAL c2g:Node"xsi:schemaLocation="http://obix.org/ns/sc/1.0/obix/xsd" href="N0014E1/"></ref>
</obj>
C# code:
public static string NodePath = "http://" + MainClass.IpAddress + ObixPath;
public static void XMLData()
{
XmlDocument NodeValue = new XmlDocument();
NodeValue.LoadXml(NodePath);
var nodes = NodeValue.SelectNodes(NodePath);
foreach (XmlNode Node in nodes)
{
HttpContext.Current.Response.Write(Node.SelectSingleNode("//ref name").Value);
Console.WriteLine(Node.Value);
}
//Console.WriteLine(Node);
Console.ReadLine();
}
Your SelectNodes and SelectSingleNode commands are incorrect. Both expect an xpath string to identify the node.
Try the following
string xml = #"<obj is=""c2g:Network "" href=""http://192.168.2.230/obix/config/""><ref name=""N0014E1"" is=""c2g:LOCAL c2g:Node"" href=""N0014E1/""></ref></obj>";
XmlDocument NodeValue = new XmlDocument();
NodeValue.LoadXml(xml);
XmlNode r = NodeValue.SelectSingleNode("//ref[#name]");
if (r != null)
{
System.Diagnostics.Debug.WriteLine(r.Attributes["name"].Value);
}
Also, Note, that LoadXml method simply loads an xml string; it will not load from a remote url.
As #kevintdiy has pointed out your xml is not entirely correct. In the sample above I have stripped out the xsi reference as you are lacking a definition for it.
If you have access to the source xml, either remove the reference to xsi if its not required or add a definition for it to the root node.
If this is not possible, then you may want to consider using regular expression or other string based methods for getting the value.

Remove a portion of XML, edit it, then add back to xml at original position

So what I'm ultimately trying to do is parse XML and add element values to an element that is contained within a cdata section. I search for the cdata section within the xml pull it out and load it in another xdocument so as to keep the xml structure and then I add the element values but now I'm not sure how to add it back to the original xml at the original position.
Here is the original XMl:
<OUTPUT version="2.0"><RESPONSE><DATA state="FL" city="Sarasota">
<![CDATA[<LION xmlns="http://www.com" version="5.050">
<COMMENTS>
<PLACES>
Forest under a tree
</PLACES></COMMENTS></LION>]]>
</DATA></RESPONSE></OUTPUT>
I search for the cdata section and insert element values like this:
XDocument value = XDocument.Parse(returnValue);
RegexOptions options = RegexOptions.None;
Regex regex = new Regex(#"\<\!\[CDATA\[(?<text>[^\]]*)\]\]\>", options);
bool isMatch = regex.IsMatch(returnValue);
if(isMatch)
{
Match match = regex.Match(returnValue);
string HTMLtext = match.Groups["text"].Value;
XDocument cdata = XDocument.Parse(HTMLtext);
XNamespace ns = #"http://www";
var com = cdata.Descendants(ns + "COMMENTS").First();
var dcomm = com.Element(ns + "PLACES");
dcomm.Value = "test"+ dcomm.Value;
What I have left is to append back on the cdata text because that was removed converting regex to string and then place it back at the position of the cdata in the original xml.
You can test to see if the node is cdata without having to use regex using the NodeType property. In example we try to cast to XCData to test.
XElement root = XElement.Parse(input);
XElement dataElement = root.Descendants("DATA").FirstOrDefault();
XCData cdata = dataElement == null ? null : dataElement.FirstNode as XCData;
if (cdata == null)
{
return;
}
XElement nestedXml = XElement.Parse(cdata.Value);
XNamespace ns = #"http://www.com";
var com = nestedXml.Descendants(ns + "PLACES").First();
com.Value = "Incomplete App Email sent to member." + com.Value;
cdata.Value = nestedXml.ToString(SaveOptions.DisableFormatting);
string updatedOutput = cdata.ToString();

Extract contents from XML file

I have a C# application that uses a button to generate a file. Currently, I want to use C# to extract out contents from the XML file and pass it as a string. For example in my XML file, I have a tag name. I want to use c# to extract the name from the XML file. How should I go about achieving it? Below is the sample code I have currently. The entire process must be carried out using a button click.
private void button1_Click(object sender, EventArgs e)
{
XElement xml = XElement.Load("C:\\Windows 7.xml");
IEnumerable<XElement> propertyIDs = xml.Descendants("PropertyId");
foreach (XElement child in xml.Elements())
{
XElement row = child.Element("my:VM_Name");
string test = xml.ToString();
Console.WriteLine(test);
}
}
Please access this link to view my xml file: http://pastebin.com/NKhBb4Zh
I rewrote your example and changed it to make use of the XmlDocument class. As there is the my Namespace I had to add a NameSpaceManager. using this you may even select a spefic node.
string url = #"e:\temp\data.xml";
XmlDocument doc = new System.Xml.XmlDocument();
doc.Load(url);
XmlElement docElement = doc.DocumentElement;
/// loop through all childNodes
foreach (XmlNode childNode in docElement.ChildNodes)
{
Console.WriteLine(childNode.Name + ": " + childNode.InnerText);
}
XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);
mgr.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2011-05-27T03:57:48");
/// use the given XmlNamespaceManager to select a specific element
XmlNode node = docElement.SelectSingleNode("my:VM_DiskSize", mgr);
/// use innerText for node text and value for attributes only
Console.WriteLine("\n" + node.Name + ": " + node.InnerText);
hth
The comments you added to your question were very helpful. In particular:
I added this code:
XElement name = xml.Element("my:VM_Name");
string test = xml.ToString();
Console.WriteLine(test);
But I am still unable to extract out Windows 7 from the XML tag
And:
i get this error The ':' character, hexadecimal value 0x3A, cannot be included in a name.
Let's start with the error first. You cannot pass to the Element method an ns:name pair as you've done. With this API, the namespace (ns) must be supplied programatically via the XName type. So instead, that line should read:
XElement name = xml.Element(XName.Get("VM_Name", "my"));
Here we pass the qualified name as an actual XName and not as a colon-delimited string as it originates. Pay attention to the order; the namespace comes second using this syntax.
Now, once you have done all this, the other line in which you have a problem is:
string test = xml.ToString();
Here, xml refers to your root XML node whereas what you actually want is, presumably, the element for which you just queried: xml.Element(XName.Get("VM_Name", "my")). Furthermore, to get the text contents of that node, you should use the Value property. I suspect what you really want is:
string test = name.Value;

Categories