How to select something within an XML attribute? - c#

I am currently attempting to replace a certain string in an xml document. I am doing this through Visual Studio using C#. The exact string I want to replace is Data Source = some-host to Data Source = local-host. The string is located under an attribute to my Strings. However, the attribute connectionString has many values under it.
<Strings>
<add name="Cimbrian.Data.ConnectionString" connectionString="Data Source=some-host;Integrated Security=false;pooling=true;Min Pool Size=5;Max Pool Size=400;Connection Timeout=5;"/>
I have managed to be able to select and replace the entire values for both name and connectionString however I want to be able to select JUST the Data Source = some-host to replace.
After loading the document my code currently looks like this,
XmlNode ConnectNode = Incident.SelectSingleNode("//Strings");
XmlNode add1 = ConnectNode.FirstChild;
add1.Attributes[1].Value = "THIS REPLACES ALL OF CONNECTION STRING";
But as the string value suggests, it is replacing far more than I want it to. Any help would be appreciated. Apologies if that is slightly hard to follow.
EDIT - I forgot to mention that if possible I want to do this without searching for the specific string Data Source = some-host due to the fact that the some-host part may change, and I still want to be able to edit the value without having to change my code.

This has really nothing to do with XML - the fact that the value of the attribute is itself a semi-colon-separated list is irrelevant as far as XML is concerned. You'd have the same problem if you had the connection string on its own.
You can use SqlConnectionStringBuilder to help though:
var builder = new SqlConnectionStringBuilder(currentConnectionString);
builder.DataSource = "some other host";
string newConnectionString = builder.ToString();
This means you don't need to rely on the current exact value of some-host (and spacing) which you will do if you just use string.Replace.

If you know exactly what you would be replacing you could use the replace method:
string string2 = string1.Replace("x", "y");
This would find all instances of x and replace them with y in string1
EDIT:
Your specific code would look something like this:
add1.Attributes[1].Value = add1.Attributes[1].Value.Replace("Data Source = some-host","Data Source = local-host");
EDIT 2:
Okay based on your comment I would then split the string on the semi-colon and then iterate to find the DataSource string and modify it and then concatenate everything back together

Related

JSON Data format to remove escaped characters

Having some trouble with parsing some JSON data, and removing the escaped characters so that I can then assign the values to a List. I've read lots of pages on SO about this very thing, and where people are having success, I am just now. I was wondering if anyone could run their eyes over my method to see what I am doing wrong?
The API I have fetching the JSON data from is from IPStack. It allows me to capture location based data from website visitors.
Here is how I am building up the API path. The two querystrings i've added to the URI are the access key that APIStack give you to use, as well as fields=main which gives you the main location based data (they have a few other blocks of data you can also get).
string api_URI = "http://api.ipstack.com/";
string api_IP = "100.121.126.33";
string api_KEY = "8378273uy12938";
string api_PATH = string.Format("{0}{1}?access_key={2}&fields=main", api_URI, api_IP, api_KEY);
The rest of the code in my method to pull the JSON data in is as follows.
System.Net.WebClient wc = new System.Net.WebClient();
Uri myUri = new Uri(api_PATH, UriKind.Absolute);
var jsonResponse = wc.DownloadString(myUri);
dynamic Data = Json.Decode(jsonResponse);
This gives me a JSON string that looks like this. (I have entered on each key/value to show you the format better). The IP and KEY I have obfuscated from my own details, but it won't matter in this summary anyway.
"{
\"ip\":\"100.121.126.33\",
\"type\":\"ipv4\",
\"continent_code\":\"OC\",
\"continent_name\":\"Oceania\",
\"country_code\":\"AU\",
\"country_name\":\"Australia\"
}"
This is where I believe the issue lies, in that I cannot remove the escaped characters. I have tried to use Regex.Escape(jsonResponse.ToString()); and whilst this does not throw any errors, it actually doesn't remove the \ characters either. It leaves me with the exact same string that went into it.
The rest of my method is to create a List which has one public string (country_name) just for limiting the scope during the test.
List<IPLookup> List = new List<IPLookup>();
foreach (var x in Data)
{
List.Add(new IPLookup()
{
country_name = x.country_name
});
}
The actual error in Visual Studio is thrown when it tries to add country_name to the List, as it complains that it does not contain country_name, and i'm presuming because it still has it's backslash attached to it?
Any help or pointers on where I can look to fix this one up?
Resolved just from the questions posed by Jon and Luke which got me looking at the problem from another angle.
Rather than finish my method in a foreach statement and trying to assign via x.something,,, I simple replaced that block of code with the following.
List<IPLookup> List = new List<IPLookup>();
List.Add(new IPLookup()
{
country_name = Data.country_name,
});
I can now access the key/value pairs from this JSON data without having to try remove the escaped characters that my debugger was showing me to have...

c# remove (null) from XML tags

I need to figure out a good way using C# to parse an XML file for (NULL) and remove it from the tags and replace it with the word BAD.
For example:
<GC5_(NULL) DIRTY="False"></GC5_(NULL)>
should be replaced with
<GC5_BAD DIRTY="False"></GC5_BAD>
Part of the problem is I have no control over the original XML, I just need to fix it once I receive it. The second problem is that the (NULL) can appear in zero, one, or many tags. It appears to be an issue with users filling in additional fields or not. So I might get
<GC5_(NULL) DIRTY="False"></GC5_(NULL)>
or
<MH_OTHSECTION_TXT_(NULL) DIRTY="False"></MH_OTHSECTION_TXT_(NULL)>
or
<LCDATA_(NULL) DIRTY="False"></LCDATA_(NULL)>
I am a newbie to C# and programming.
EDIT:
So I have come up with the following function that while not pretty, so far work.
public static string CleanInvalidXmlChars(string fileText)
{
List<char> charsToSubstitute = new List<char>();
charsToSubstitute.Add((char)0x19);
charsToSubstitute.Add((char)0x1C);
charsToSubstitute.Add((char)0x1D);
foreach (char c in charsToSubstitute)
fileText = fileText.Replace(Convert.ToString(c), string.Empty);
StringBuilder b = new StringBuilder(fileText);
b.Replace("", string.Empty);
b.Replace("", string.Empty);
b.Replace("<(null)", "<BAD");
b.Replace("(null)>", "BAD>");
Regex nullMatch = new Regex("<(.+?)_\\(NULL\\)(.+?)>");
String result = nullMatch.Replace(b.ToString(), "<$1_BAD$2>");
result = result.Replace("(NULL)", "BAD");
return result;
}
I have only been able to find 6 or 7 bad XML files to test this code on, but it has worked on each of them and not removed good data. I appreciate the feedback and your time.
In general, regular expressions are not the right way of handling XML files. There's a range of solutions to handle XML files correctly - you can read up on System.Xml.Linq for a good start. If you're a newbie, it's certainly something you should learn at some point. As Ed Plunkett pointed out in the comments, though, your XML is not actually XML: ( and ) characters are not allowed in XML element names.
Since you will have to do it as an operation on a string, Corak's comment to use
contentOfXml.Replace("(NULL)", "BAD");
may be a good idea, but will break if any elements can contain the string (NULL) as anything other than their name.
If you want a regex approach, this might work decently, but I'm not sure if it's not missing any edge cases:
var regex = new Regex(#"(<\/?[^_]*_)\(NULL\)([^>]*>)");
var result = regex.Replace(contentOfXml, "$1BAD$2");
Will it be suitable for you to read this XML as a string and perform a regex replacement? Like:
Regex nullMatch = new Regex("<(.+?)_\\(NULL\\)(.+?)>");
String processedXmlString = nullMatch.Replace(originalXmlString, "<$1_BAD$2>");

Settings.settings save Pattern type

I am trying to define Patterns on which an E-Mail is saved, in code i have been quite succesfull doing it like this:
string Pattern0 = SafeFileName(currMail.SenderName);
string Pattern1 = string.Join(" ", currMail.Subject.Split(' ').Take(3).ToArray());
string Pattern2 = (Convert.ToDateTime(currMail.CreationTime).ToString(" dd-MMM-yyyy HH-mm"));
and then i tried to save the Patterns on Properties.Setting, I Chose string, but that makes everything a string, as opposed to making them useful like they are above.
Does anybody know what it is that i could use, to save the Settings above in the Settings.settings file.
Thanks a lot in Advance

C#: Help polishing string parsing

One of the things I really value from this community is learning ways to do things in two lines that would normally take me twenty. In that spirit, I've done my best to take some string parsing down from about a dozen lines to three. But I feel like there's someone out there who wants to show me how this is actually a mess. Just for my own edification, is there a cleaner way to do the following? Could it all be done in one line?
string getThis = "<add key=\"messageFilter\" value=\"";
string subStr = strFile.Substring(strFile.IndexOf(getThis) + getThis.Length);
string[] igPhrases = subStr.Substring(0, subStr.IndexOf(";\"")).Split(';');
UPDATE
Thanks for the quick responses! Really helpful examples AND good advice with a minimum of snark. :) Fewer lines is not the same thing as clean and elegant, and reducing lines may actually make the code worse.
Let me rephrase the question.
I've got an XML doc that has the following line: <add key="messageFilter" value="Out of Office AutoReply;Automatic reply;"/>. This doc tells our automated ticketing system not to create tickets from emails that have those phrases in the subject line. Otherwise, we get an endless loop.
I'm working on a small program that will list phrases already included, and then allow users to add new phrases. If we notice that a new autoreply message is starting to loop through the system, we need to be able to add the language of that message to the filter.
I don't work a lot with XML. I like Sperske's solution, but I don't know how to make it dynamic. In other words, I can't put the value in my code. I need to find the key "messageFilter" and then get all the values associated with that key.
What I've done works, but it seems a little cumbersome. Is there a more straightforward way to get the key values? And to add a new one?
A slightly different one liner (split for readability):
System.Xml.Linq.XDocument
.Parse("<add key='messageFilter' value='AttrValue'/>")
.Root
.Attribute("value")
.Value
Outputs:
AttrValue
To address the updated question you could turn all of your <add> nodes into a dictionary (borrowing from Pako's excellent answer, and using a slightly longer string):
var keys = System.Xml.Linq.XDocument
.Parse("<keys><add key='messageFilter' value='AttrValue'/><add key='userFilter' value='AttrValueUser'/></keys>")
.Descendants("add")
.ToDictionary(r => r.Attribute("key").Value, r => r.Attribute("value").Value);
This lets you access your keys like so:
keys["messageFilter"] == "AttrValue"
keys["userFilter"] == "AttrValueUser"
It has been answered already, but for future readers - if you want to parse bigger XML, with root and many add nodes, you may need to use something slightly different.
string xmlPart = "<add key=\"messageFilter\" value=\"\" />";
string xml = "<root>" + xmlPart + "</root>";
var x = XDocument.Parse(xmlPart, LoadOptions.None);
var attributes1 = x.Descendants("add").Select(n => n.Attributes());
var attributes2 = x.Descendants("add").SelectMany(n => n.Attributes());
This will get you IEnumerable<IEnumerable<XAttribute>> (see attributes1) or IEnumerable<XAttribue> (see attributes2). Second option will simply flatten results - all attributes will be held in one collection, no matter from which node they came from.
Of course nothing stops you to filter XAttributes by name or some other criteria - it all up to you!
one ugly line:
string[] igPhrases = strFile.Substring(strFile.IndexOf(getThis) + ("<add key=\"messageFilter\" value=\"").Length).Substring(0, strFile.Substring(strFile.IndexOf("<add key=\"messageFilter\" value=\"") + ("<add key=\"messageFilter\" value=\"").Length).IndexOf(";\"")).Split(';');

Find and replace question regarding RegEx.Replace

I have a text file and I want to be able to change all instances of:
T1M6 to N1T1M6
The T will always be a different value depending on the text file loaded. So example it could sometimes be
T2M6 and that would need to be turned into N2T2M6. The N(value) must match the T(value). The M6 will always be M6.
Another example:
T9M6 would translate to N9T9M6
Here is my code to do the loading of the text file:
StreamReader reader = new StreamReader(fDialog.FileName.ToString());
string content = reader.ReadToEnd();
reader.Close();
Here is RegEx.Replace statement that I came up with. Not sure if it is right.
content = Regex.Replace(content, #"(T([-\d.]))M6", "N1$1M6");
It seems to work at searching for T5M6 and turning it into N1T5M6.
But I am unsure how to turn the N(value) into the value that T is. For example N5T5M6.
Can someone please show me how to do modify my code to handle this?
Thanks.
Like this:
string content = File.ReadAllText(fDialog.FileName.ToString());
content = Regex.Replace(content, #"T([-\d.])M6", "N$1T$1M6");
Also, you should probably replace [-\d.] with \d or -?\d\.?

Categories