Escaping and double quotes together with Linq - c#

I am making a small piece of code where I look for all nodes in XML containing "folder name=\"u"" .
I have problems with the string literals, I tried with # and escape or double quotes without any success. Here is the code :
public class Folders
{
public static IEnumerable<string> FolderNames(string xml, char startingLetter)
{
string[] MyString;
List<string> MyList = new List<string>();
string item = "";
StringSplitOptions.None)).ToList();
MyString = xml.Split('>') ;
var matchingvalues = MyString
.Where(stringToCheck => stringToCheck.Contains("<folder name=\\\""));
return matchingvalues;
}
public static void Main(string[] args)
{
string xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<folder name=\"c\">" +
"<folder name=\"program files\">" +
"<folder name=\"uninstall information\" />" +
"</folder>" +
"<folder name=\"users\" />" +
"</folder>";
foreach (string name in Folders.FolderNames(xml, 'u'))
Console.WriteLine(name);
Console.ReadLine();
}
How should I write
var matchingvalues = MyString.Where(stringToCheck => stringToCheck.Contains("
?

You're not even using your startingLetter parameter in FolderNames
You say you're looking for "folder name=\"u"", but your code looks for "<"folder name=\\\"". Disregarding the missing "u", you're looking for a literal backslash as well. Which doesn't exist in your xml. The backslashes in your xml are for escaping the quotes.
You haven't posted your real code because your method doesn't even work. WTF is this??
StringSplitOptions.None)).ToList();
You don't use the item variable.
Hopefully the above is enough to show where you went wrong. Better still, use .NET's xml parsing abilities to get the values. Currently your method lies; it doesn't just return "Folder Names", it returns a mess of half-xml as well.

The main problem isn't really with the escaping.
But with the fact you reinvented the wheel a little bit.
There are multiple xml parsers in c#.
Linq to xml is one of them. with it you could write something simple like:
string xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<folder name=\"c\">" +
"<folder name=\"program files\">" +
"<folder name=\"uninstall information\" />" +
"</folder>" +
"<folder name=\"users\" />" +
"</folder>";
XElement xElement = XElement.Parse(xml);
IEnumerable<string> values = xElement.
Descendants("folder").
Where(element => element.Attribute("name")?.Value?.StartsWith("u") == true).
Select(element => element.Attribute("name").Value);

Related

Convert unformatted string (not valid json) to json

I have a string which I get from a webservice which looks like this:
({
id=1;
name="myName";
position="5";
})
which is not a parsable json. I wanted to ask if there are any ways besides going character to character and correcting them to convert such string into a parsable json like this:
{
"id":1,
"name":"myName",
"position":"5"
}
Check this link it will be helpful :
https://forum.unity3d.com/threads/json-web-services.366073/
You cold run a bunch of regex replaces for each change. But you'll need captures for the property names etc The performance will be horrible.
If the format is known and reliable (eg what happens with collections/arrays and sub-objects). And the service provider does not provide a client or SDK. Then your best bet is to write your own parser. It's not that hard to create your own from scratch. Or you can use a parser library like Irony.net or eto.parse. Both of these allow you to construct a grammar in c# so it is fully self contained without the need for compiler-compilers and generated code. There is also a class of parser called "monadic" parsers like Sprache which are of a simpler nature (once you wrap your head around them).
Whichever approach is taken you'll end up with a way of recognising each property and object boundary where you can do what you need to do: set a property; create a JToken; whatever...
Then you can wrap the whole lot in a MediaTypeFormatter and call the service via HttpClient and get objects out.
Finally I had to write my own function to convert it to a parsable json, here's the function I wrote:
public string convertToJson(string mJson)
{
mJson = mJson.Replace("(","[");
mJson = mJson.Replace(")","]");
string mJson2 = mJson.Trim('[',']');
string[] modules = mJson2.Split(',');
for(int i = 0;i<modules.Length;i++)
{
Debug.Log("module["+i+"]: " + modules[i]);
}
for(int m=0;m<modules.Length;m++)
{
char[] mCharacter = {'{','}'};
modules[m] = modules[m].Replace("{",string.Empty).Replace("}",string.Empty).Trim();
Debug.Log("module["+m+"] after trim: " + modules[m]);
string[] items = modules[m].TrimEnd(';').Split(';');
modules[m] = "{";
for(int j=0;j<items.Length;j++)
{
Debug.Log("item["+j+"]: " + items[j]);
string[] keyValue = items[j].Split('=');
Debug.Log("key,value: " + keyValue[0] + ", " + keyValue[1]);
modules[m] = modules[m] + "\"" + keyValue[0].Trim() + "\":" + keyValue[1].Trim() + ",";
}
modules[m] = modules[m].Substring(0,modules[m].Length-1) + "}";
Debug.Log("modules["+m+"] final: " + modules[m]);
}
string finalJson = "[";
for(int m=0;m<modules.Length;m++)
{
finalJson = finalJson + modules[m] + ",";
}
finalJson = finalJson.Substring(0,finalJson.Length-1) + "]";
Debug.Log("finalJson: " + finalJson);
return finalJson;
}

C# Parallel.Foreach with XML

I'm brand new to C# though have some minor experience with other languages and have hit a brick wall.
The code below works exactly as expected:
XmlDocument doc = new XmlDocument();
doc.Load("config.xml");
foreach (XmlNode node in doc.DocumentElement.ChildNodes)
{
string name = node.Attributes["name"].Value;
string ips = node.Attributes["ip"].Value;
string port = node.Attributes["port"].Value;
Console.WriteLine(name + " | " + ips + ":" + port);
}
I get out exactly what I am expecting with zero errors, however the following code has got me stumped. I am hoping someone can explain what I am doing wrong as I feel like I am perhaps missing something fundamental.
XmlDocument doc = new XmlDocument();
doc.Load("config.xml");
node = doc.DocumentElement.ChildNodes;
Parallel.ForEach(node,
(item) => {
string name = item.Attributes["name"].Value;
string ips = item.Attributes["ip"].Value;
string port = item.Attributes["port"].Value;
Console.WriteLine(name + " | " + ips + ":" + port);
});
I am simply trying to run each iteration of the loop in parallel. When I try compile I get the following error:
CS0411 The type arguments for method 'Parallel.ForEach
(IEnumerable, Action)' cannot be inferred from the usage.
Try specifying the type arguments explicitly.
Example XML below:
<item name="pc01" ip="192.168.0.10" port="80"><!--PC01--></item>
<item name="pc02" ip="192.168.0.11" port="80"><!--PC02--></item>
<item name="pc03" ip="192.168.0.12" port="80"><!--PC03--></item>
<item name="pc04" ip="192.168.0.13" port="80"><!--PC04--></item>
Any assistance would be greatly appreciated.
You need to Cast non generic types. Full solution below.
static void Main(string[] args)
{
var xml="<root><item name='pc01' ip='192.168.0.10' port='80'><!--PC01--></item><item name='pc02' ip='192.168.0.11' port='80'><!--PC02--></item><item name='pc03' ip='192.168.0.12' port='80'><!--PC03--></item><item name='pc04' ip='192.168.0.13' port='80'><!--PC04--></item></root>";
XmlDocument doc=new XmlDocument();
// doc.Load("config.xml");
doc.LoadXml(xml);
var nodeList=doc.DocumentElement.ChildNodes;
Parallel.ForEach(nodeList.Cast<XmlNode>(),
item => {
string name=item.Attributes["name"].Value;
string ips=item.Attributes["ip"].Value;
string port=item.Attributes["port"].Value;
Console.WriteLine(name + " | " + ips + ":" + port);
});
Console.ReadLine();
}
Are the items displaying in the console out of sequence?
You can safely call Console.WriteLine from multiple threads but I wouldn't count on the items actually getting written to the console in the expected sequence. I'd expect them to usually get written in the expected sequence and then sometimes not. That's the behavior of multithreaded execution. It will do what you expect it to do, but never count on it happening in the expected sequence, even if you test over and over and over and it does happen in the expected sequence.

Write html anchor text (with name / value pair) to a string

I think I can't see the forest for the trees here. I want to write a string. Using Linq to SQL I have created a result and I'm looping through it to dynamically write anchor tags.
But, the code is producing this:
<a 45="" href="ADappointment.aspx?openingid">My person booked< /a >
I want:
<a href="ADappointment.aspx?openingid=45">My person booked< /a >
Here's what I'm doing:
foreach (var anOpening in results)
string sFlag = #"";
sFlag = #"<td>" + patient.FirstName + " " + patient.LastName + " accepted </td>";
...
What am I doing wrong?
You have quotes in your href value the browser doesnt expect.. so it is rendering it completely incorrectly.
You are producing this:
href="urlhere.aspx?id="99""
Note the quotes around the ID. Remove those from your code. You want something like this:
sFlag = #"<td><a href=""ADappointment.aspx?openingid=" + anOpening.OpeningId + """>" + /* the rest here */
Ideally you would use a library to do this. There is a TagBuilder class in the MVC assembly.
Use String.Format to clearly format string.
foreach (var anOpening in results)
{
var sFlag = String.Format(#"<td>{1} {2} accepted </td>", anOpening.OpeningId, patient.FirstName, patient.LastName);
}

Read special characters back from XmlDocument in c#

Say I have the an xml with a escaped ampersand (&). How do I then read it back such that the result gives me the 'un-escaped' text.
Running the following gives me "&amp" as the result. How do I get back '&'
void Main()
{
var xml = #"
<a>
&
</a>
";
var doc = new XmlDocument();
doc.LoadXml(xml);
var ele = (XmlElement)doc.FirstChild;
Console.WriteLine (ele.InnerXml);
}
Use ele.InnerText instead of ele.InnerXml
you can use CDATA in order to get your data
Characters like "<" and "&" are illegal in XML elements."<" the parser interprets it as the start of a new element. "&" the parser interprets it as the start of an character entity.
use
HttpServerUtility.HtmlDecode (ele.InnerXml);
Console.WriteLine (HttpUtility.UrlDecode(String.Format("{0}",ele.InnerXml)));
Try using this static method to decode escaped characters:
HttpServerUtility.HtmlDecode Method (String)
See example here:
http://msdn.microsoft.com/ru-ru/library/hwzhtkke.aspx
The following characters are illegal in XML elements:
Illegal EscapedUsed
------------------
" "
' &apos;
< <
> >
& &
To get the unescaped value you can use:
public string UnescapeXMLValue(string xmlValue)
{
if (string.IsNullOrEmpty(s)) return s;
string temp = s;
temp = temp.Replace("&apos;", "'").Replace(""", "\"").Replace(">", ">").Replace("<", "<").Replace("&", "&");
return temp ;
}
To get the escaped value you can use:
public string EscapeXMLValue(string value)
{
if (string.IsNullOrEmpty(s)) return s;
string temp = s;
temp = temp.Replace("'","&apos;").Replace( "\"", """).Replace(">",">").Replace( "<","<").Replace( "&","&");
return temp ;
}

Read Lines in Text File then See if XML Files Contain Those Words Appeared in Text File

Please bear with me as I am very new to programming itself and C# winforms.
I have an AAA.txt file where I make it appears in a combobox as "AAA". My main intention is to allow user to choose AAA from the dropdown combo, then click search. On the click event, the function should read the text file's content line by line and then find if these words (eg hello) or phrases (eg good morning) appear in all my 20 XML files' <description></description> child nodes. If these words/phrases do appear in certain <description></description> child nodes, then the data of whole <item></item> parent nodes will appear as results.
AAA.txt:
hello
good morning
great
bye
My function:
private void searchComByKeywords()
{
string[] fileEntries = Directory.GetFiles(sourceDir);
foreach (string fileName in fileEntries)
{
XmlDocument xmlDoc = new XmlDocument();
string docPath = fileName;
xmlDoc.Load(docPath);
XmlNodeList nodeList = xmlDoc.GetElementsByTagName("item");
foreach (XmlNode node in nodeList)
{
XmlElement itemElement = (XmlElement)node;
string itemDescription = itemElement.GetElementsByTagName("description")[0].InnerText;
if (itemDescription.ToLower().Contains(comboTemplates.SelectedItem.ToString()))
{
string itemTitle = itemElement.GetElementsByTagName("title")[0].InnerText;
string itemDate = itemElement.GetElementsByTagName("pubDate")[0].InnerText;
string itemAuthor = itemElement.GetElementsByTagName("author")[0].InnerText;
richComByTemplate.AppendText("Author: " + itemAuthor + "\nDate: " + itemDate + "\nTitle: " + itemTitle + "\nDescription: " + itemDescription + "\n\n--------\n\n");
}
}
}
}
I understand some may tell me to use LINQ-to-XML, but this is not my concern at this point. I know this line if (itemDescription.ToLower().Contains(comboTemplates.SelectedItem.ToString())) doesn't do what I want (it will search the word "AAA" instead of looking into the selected AAA text file). May I know how can I write this line correctly in order to read the words/phrases appear in the selected text file?
Thank you.
The static System.IO.File class has a method ReadAllLines that reads all the lines of a text file into an array.
string[] words = File.ReadAllLines(filepath);
If the combo contains only the filename, you might want to supplement it with the directory name first
string dir = #"C:\MyDataPath";
string filename = comboTemplates.SelectedItem.ToString();
string filepath = Path.Combine(dir, filename);
Then put the words into a HashSet<string>
var wordSet = new HashSet<string>(words);
Then split your description into single words using a regular expression
var descrWords =
new HashSet<string>(
Regex.Matches(itemDescription.ToLower(), #"\w+")
.Cast<Match>()
.Select(m => m.Value)
);
descrWords.UnionWith(wordSet);
if (descrWords.Count > 0) {
// Your description contains at least one of the words
}
You can do the comparison in many different ways. E.g. by using LINQ
if (words.Union(
Regex.Matches(itemDescription.ToLower(), #"\w+")
.Cast<Match>()
.Select(m => m.Value)
).Any())
{
...
}
Note: It is not enough to look if a string contains a word with
s.Contains("great")
since it would find parts of words like "greatness" as well.
If you need to find phrases as well, the approach described above does not work. You will need to combine a Regex search with a loop or a LINQ statement. Let's use a regex expression of the type
\bWordOrPhrase\b
\b matches word boundaries. In order to be sure not to introduce some special regex character into the regex expression we need to escape our word or phrase.
bool found = Regex.IsMatch(description, #"\b" + Regex.Escape(wordOrPhrase) + #"\b");
Finally we must do this test for all the word and phrases in the list. Let's put everything together:
string dir = #"C:\MyDataPath";
string filename = comboTemplates.SelectedItem.ToString();
string filepath = Path.Combine(dir, filename);
string[] words = File.ReadAllLines(filepath);
Then test your descriptions
string itemDescription = itemElement.GetElementsByTagName("description")[0].InnerText;
if (words.Any(
wordOrPhrase =>
Regex.IsMatch(itemDescription,
#"\b" + Regex.Escape(wordOrPhrase) + #"\b",
RegexOptions.IgnoreCase)))
{
...
}

Categories