XDocument changes tab to space - c#

I have a xml-document that simplified looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Node1 separator=" " />
There is a \t as attribute value.
When executing this code
var path = #"C:\test.xml";
var doc = XDocument.Load(path);
doc.Save(path);
the attribute value changed from tab to space.
<?xml version="1.0" encoding="utf-8"?>
<Node1 separator=" " />
Is there a way to preserve the origin value, because it is required to be a tab?

This is "XML whitespace normalization in attributes" portion of XML:Attribute-Value Normalization which is default behavior when handling XML documents.
For a white space character (#x20, #xD, #xA, #x9), append a space character (#x20) to the normalized value
You should be able to use XmlTextReader.Normalization property as described here. XmlDocument can load from reader XmlDocument.Load.
var path = #"C:\test.xml";
XmlDocument doc = new XmlDocument();
XmlTextReader reader = new XmlTextReader(path);
doc.Load(reader);
var s = doc.SelectSingleNode("*/#*").InnerText;
Console.WriteLine("|{0}|, {1}", (int)s[0], s.Length); // prints 9 - ASCII code of tab
doc.Save(path);

Related

How would one add a string value to a newly created xml file in c#?

In my code I'm creating a new xml file with linq to xml and I have a specific format of xml that I'm trying to put into the xml file on creation. However, when I put the string variable in it gives the error "non white space characters cannot be added to content." How would I correctly add that string value to the xml file?
string firstPart = #"<?xml version=""1.0"" encoding=""utf-8""?>
< wiidisc version = ""1"" >
< id game = ""RMCE"" disc = ""0"" version = ""0"" >
</ id > ";
XDocument doc = new XDocument(firstPart);
doc.Save(riivolutionXmls + #"\" + xmlFileName + ".xml");
This isn't valid XML, you're missing a closing tag for wiidisc.
Also I don't think you can use the constructor to create an XDocument from a string, I think you have to use the XDocument.Parse method:
https://learn.microsoft.com/en-us/dotnet/api/system.xml.linq.xdocument.parse?view=netframework-4.7.2#System_Xml_Linq_XDocument_Parse_System_String_

IgnoreWhiteSpace not ignoring whitespace at beginning of xml string

Question
Should whitespace be ignored at the beginning of my multi-line string literal xml?
Code
string XML = #"
<?xml version=""1.0"" encoding=""utf-8"" ?>"
using (StringReader stringReader = new StringReader(XML))
using (XmlReader xmlReader = XmlReader.Create(stringReader,
new XmlReaderSettings() { IgnoreWhitespace = true }))
{
xmlReader.MoveToContent();
// further implementation withheld
}
Notice in the above code that there is white space before the XML declaration, this doesn't seem to be being ignored despite my setting of the IgnoreWhiteSpace property. Where am I going wrong?!
Note: I have the same behaviour when the XML string does not have a line break, and just a whitespace, as below. I know this will run if I remove the whitespace, my question is as to why the property doesn't take care of this?
string XML = #" <?xml version=""1.0"" encoding=""utf-8"" ?>"
The documentations say that the IgnoreWhitespace property will "Gets or sets a value indicating whether to ignore insignificant white space.". While that first whitespace (and also linebreak) should be insignificant, the one who made XmlReader apparently didn't think so. Just trim XML before use, and you'll be fine.
As stated in comments and for clarity, change your code to:
string XML = #"<?xml version=""1.0"" encoding=""utf-8"" ?>"
using (StringReader stringReader = new StringReader(XML.Trim()))
using (XmlReader xmlReader = XmlReader.Create(stringReader,
new XmlReaderSettings() { IgnoreWhitespace = true }))
{
xmlReader.MoveToContent();
// further implementation withheld
}
According to Microsoft's documentation regarding XML Declaration
The XML declaration typically appears as the first line in an XML
document. The XML declaration is not required, however, if used it
must be the first line in the document and no other content or white
space can precede it.
The parse should fail for your code because white space precedes the XML declaration. Removing either the white space OR the xml declaration will result in a successful parse.
In other words it would be a bug if XmlReaderSettings were at odds with the documentation for XML Declaration - it is defined behavior.
Here's some code demonstrating the above rules.
using System;
using System.Web;
using System.Xml;
using System.Xml.Linq;
public class Program
{
public static void Main()
{
//The XML declaration is not required, however, if used it must
// be the first line in the document and no other content or
//white space can precede it.
// here, no problem because this does not have an XML declaration
string xml = #"
<xml></xml>";
XDocument doc = XDocument.Parse(xml);
Console.WriteLine(doc.Document.Declaration);
Console.WriteLine(doc.Document);
//
// problem here because this does have an XML declaration
//
xml = #"
<?xml version=""1.0"" encoding=""utf-8"" ?><xml></xml>";
try
{
doc = XDocument.Parse(xml);
Console.WriteLine(doc.Document.Declaration);
Console.WriteLine(doc.Document);
} catch(Exception e) {
Console.WriteLine(e.Message);
}
}
}

Xml Reading Issue using Xdocument

Below is the sample xml,
<?xml version="1.0" encoding="utf-8"?>
<UsersList>
<User>
<Name>sam&Tim</Name>
<Address>21, bills street, CA</Address>
<Issues>"Issues1", "Issues2"</Issues>
</User>
</UsersList>
c#:
string xml = System.IO.File.ReadAllText(#"E:\Sample.xml");
xml = System.Text.RegularExpressions.Regex.Replace(xml, "<(?![_:a-z][-._:a-z0-9]*\b[^<>]*>)", "<");
XDocument doc = XDocument.Parse(xml);
i need to convert the special charecters (<,>,",',&) and i am using the above regex. but parse method throws an error. any help please how to resolve the issue
See your current code converts XML like this
<?xml version="1.0" encoding="utf-8"?>
<UsersList>
<User>
<Name>sam&Tim</Name>
<Address>21, bills street, CA</Address>
<Issues>"Issues1", "Issues2"</Issues>
</User>
</UsersList>
Whereas Parse is looking it like this
<?xml version="1.0" encoding="utf-8"?>
<UsersList>
<User>
<Name>sam and Tim</Name>
<Address>21, bills street, CA</Address>
<Issues>"Issues1", "Issues2"</Issues>
</User>
</UsersList>
and thus you should not be converting < to < but XML contains sam&Tim would not allow you to Parse it. thus you can use
xml = xml.Replace("&", " n ");//n or and or some other char or string you want
instead of
xml = System.Text.RegularExpressions.Regex.Replace(xml, "<(?![_:a-z][-._:a-z0-9]*\b[^<>]*>)", "<");
Hope this will help you to parse it.
You can give a try with:
string xml = System.IO.File.ReadAllText(#"E:\Sample.xml");
xml = ReplaceXMLEncodedCharacters(xml)
public string ReplaceXMLEncodedCharacters(string input)
{
const string pattern = #"&#(x?)([A-Fa-f0-9]+);";
MatchCollection matches = Regex.Matches(input, pattern);
int offset = 0;
foreach (Match match in matches)
{
int charCode = 0;
if (string.IsNullOrEmpty(match.Groups[1].Value))
charCode = int.Parse(match.Groups[2].Value);
else
charCode = int.Parse(match.Groups[2].Value, System.Globalization.NumberStyles.HexNumber);
char character = (char)charCode;
input = input.Remove(match.Index - offset, match.Length).Insert(match.Index - offset, character.ToString());
offset += match.Length - 1;
}
return input;
}
Your problem is that your original XML is not a valid XML document, because is contains an unescaped ampersand ('&') which is explicitly forbidden by the standard that says
The ampersand character (&) and the left angle bracket (<) must not appear in their literal form, except when used as markup delimiters, or within a comment, a processing instruction, or a CDATA section.
To make it valid, you must use &amp instead of a literal &. Trying to "correct" it is not practical and a totally bad idea in the general case, because you can never be sure, where in your XML & stands for a literal & and where it is part of an XML entity. If it were possible to distinguish these usages unambiguously, that rule could be embedded in XML parsers and we would not have to deal with it.
A valid, standard-conformant representation of your document would be
<?xml version="1.0" encoding="utf-8"?>
<UsersList>
<User>
<Name>sam&Tim</Name>
<Address>21, bills street, CA</Address>
<Issues>"Issues1", "Issues2"</Issues>
</User>
</UsersList>

How to use replace with tricky characters in C#?

I am trying to replace within a string
<?xml version="1.0" encoding="UTF-8"?>
<response success="true">
<output><![CDATA[
And
]]></output>
</response>
with nothing.
The problem I am running into is the characters <> and " characters are interacting within the replace. Meaning, it's not reading those lines as a full string all together as one but breaking the string when it comes to a <> or ". Here is what I have but I know this isn't right:
String responseString = reader.ReadToEnd();
responseString.Replace(#"<<?xml version=""1.0"" encoding=""UTF-8""?><response success=""true""><output><![CDATA[[", "");
responseString.Replace(#"]]\></output\></response\>", "");
What would be the correct code to get the replace to see these lines as just a string?
A string will never change. The Replace method works as follows:
string x = "AAA";
string y = x.Replace("A", "B");
//x == "AAA", y == "BBB"
However, the real problem is how you handle the XML response data.
You should reconsider your approach of handling incoming XML by string replacement. Just get the CDATA content using the standard XML library. It's as easy as this:
using System.Xml.Linq;
...
XDocument doc = XDocument.Load(reader);
var responseString = doc.Descendants("output").First().Value;
The CDATA will already be removed. This tutorial will teach more about working with XML documents in C#.
Given your document structure, you could simply say something like this:
string response = #"<?xml version=""1.0"" encoding=""UTF-8""?>"
+ #"<response success=""true"">"
+ #" <output><![CDATA["
+ #"The output is some arbitrary text and it may be found here."
+ "]]></output>"
+ "</response>"
;
XmlDocument document = new XmlDocument() ;
document.LoadXml( response ) ;
bool success ;
bool.TryParse( document.DocumentElement.GetAttribute("success"), out success) ;
string content = document.DocumentElement.InnerText ;
Console.WriteLine( "The response indicated {0}." , success ? "success" : "failure" ) ;
Console.WriteLine( "response content: {0}" , content ) ;
And see the expected results on the console:
The response indicated success.
response content: The output is some arbitrary text and it may be found here.
If your XML document is a wee bit more complex, you can easily select the desired node(s) using an XPath query, thus:
string content = document.SelectSingleNode( #"/response/output" ).InnerText;

Doing regex style compare while looping through a XML file in C#

I have a XML file that i am using to loop through an on matching of a child node getting the value of a an attribute.The thing is matching these values with a * character or ? character like some regex style..can someone tell me how to do this .So if a request comes like g.portal.com it should match the second node .I am using .net 2.0
Below is my XML file
<Test>
<Test Text="portal.com" Sample="1" />
<Test Text="*.portal.com" Sample="201309" />
<Test Text="portal-0?.com" Sample="201309" />
</Test>
XmlDocument xDoc = new XmlDocument();
xDoc.Load(PathToXMLFile);
foreach (XmlNode node in xDoc.DocumentElement.ChildNodes)
{
if (node.Attributes["Sample"].InnerText == value)
{
}
}
What you need to do is first convert each Text attribute into a valid Regex pattern and then use it to match your input. Something like this:
string input = "g.portal.com";
XmlNode foundNode = null;
foreach (XmlNode node in xDoc.DocumentElement.ChildNodes)
{
string value = node.Attributes["Text"].Value;
string pattern = Regex.Escape(value)
.Replace(#"\*", ".*")
.Replace(#"\?", ".");
if (Regex.IsMatch(input, "^" + pattern + "$"))
{
foundNode = node;
break; //remove if you want to continue searching
}
}
After executing the above code, foundNode should contain the second node from the xml file.
So you have an XML file that sets up patterns, right? You'll want to feed those patterns into Regexes and then stream a number of requests through them. Did I get that correct?
Assuming the XML file doesn't change it only needs to be processed into according Regexes. For example *.portal.com would translate to
new Regex("\\w+\\.portal\\.com");
You'll just have to escape the dots, replace * with \\w+ and ? with \\w if i guessed the semantics of you match patterns correctly.
Look up the correct replacements at http://msdn.microsoft.com/en-us/library/az24scfc(v=vs.110).aspx

Categories