HTML Decode without System.Web - c#

I am working on a CLR Table-Valued function for SQL Server 2008 R2. I need to HTMLDecode a string at one point, but this is problematic b/c that relies on System.Web, which is not a support assembly for SQL Server.
Can anyone think of a better way to do the HTML Decode?
FYI SQL Server 2008 CLR only supports up to .NET 3.5 so system.net.webutility will not work.

Also you can use reflector to grab the code from WebUtility directly (please don't blame me for the coding style, its reflected stuff):
public class WebUtility {
private static class HtmlEntities {
private static string[] _entitiesList = new string[]
{
"\"-quot",
"&-amp",
"'-apos",
"<-lt",
">-gt",
"\u00a0-nbsp",
"¡-iexcl",
"¢-cent",
"£-pound",
"¤-curren",
"¥-yen",
"¦-brvbar",
"§-sect",
"¨-uml",
"©-copy",
"ª-ordf",
"«-laquo",
"¬-not",
"­-shy",
"®-reg",
"¯-macr",
"°-deg",
"±-plusmn",
"²-sup2",
"³-sup3",
"´-acute",
"µ-micro",
"¶-para",
"·-middot",
"¸-cedil",
"¹-sup1",
"º-ordm",
"»-raquo",
"¼-frac14",
"½-frac12",
"¾-frac34",
"¿-iquest",
"À-Agrave",
"Á-Aacute",
"Â-Acirc",
"Ã-Atilde",
"Ä-Auml",
"Å-Aring",
"Æ-AElig",
"Ç-Ccedil",
"È-Egrave",
"É-Eacute",
"Ê-Ecirc",
"Ë-Euml",
"Ì-Igrave",
"Í-Iacute",
"Î-Icirc",
"Ï-Iuml",
"Ð-ETH",
"Ñ-Ntilde",
"Ò-Ograve",
"Ó-Oacute",
"Ô-Ocirc",
"Õ-Otilde",
"Ö-Ouml",
"×-times",
"Ø-Oslash",
"Ù-Ugrave",
"Ú-Uacute",
"Û-Ucirc",
"Ü-Uuml",
"Ý-Yacute",
"Þ-THORN",
"ß-szlig",
"à-agrave",
"á-aacute",
"â-acirc",
"ã-atilde",
"ä-auml",
"å-aring",
"æ-aelig",
"ç-ccedil",
"è-egrave",
"é-eacute",
"ê-ecirc",
"ë-euml",
"ì-igrave",
"í-iacute",
"î-icirc",
"ï-iuml",
"ð-eth",
"ñ-ntilde",
"ò-ograve",
"ó-oacute",
"ô-ocirc",
"õ-otilde",
"ö-ouml",
"÷-divide",
"ø-oslash",
"ù-ugrave",
"ú-uacute",
"û-ucirc",
"ü-uuml",
"ý-yacute",
"þ-thorn",
"ÿ-yuml",
"Œ-OElig",
"œ-oelig",
"Š-Scaron",
"š-scaron",
"Ÿ-Yuml",
"ƒ-fnof",
"ˆ-circ",
"˜-tilde",
"Α-Alpha",
"Β-Beta",
"Γ-Gamma",
"Δ-Delta",
"Ε-Epsilon",
"Ζ-Zeta",
"Η-Eta",
"Θ-Theta",
"Ι-Iota",
"Κ-Kappa",
"Λ-Lambda",
"Μ-Mu",
"Ν-Nu",
"Ξ-Xi",
"Ο-Omicron",
"Π-Pi",
"Ρ-Rho",
"Σ-Sigma",
"Τ-Tau",
"Υ-Upsilon",
"Φ-Phi",
"Χ-Chi",
"Ψ-Psi",
"Ω-Omega",
"α-alpha",
"β-beta",
"γ-gamma",
"δ-delta",
"ε-epsilon",
"ζ-zeta",
"η-eta",
"θ-theta",
"ι-iota",
"κ-kappa",
"λ-lambda",
"μ-mu",
"ν-nu",
"ξ-xi",
"ο-omicron",
"π-pi",
"ρ-rho",
"ς-sigmaf",
"σ-sigma",
"τ-tau",
"υ-upsilon",
"φ-phi",
"χ-chi",
"ψ-psi",
"ω-omega",
"ϑ-thetasym",
"ϒ-upsih",
"ϖ-piv",
"\u2002-ensp",
"\u2003-emsp",
"\u2009-thinsp",
"‌-zwnj",
"‍-zwj",
"‎-lrm",
"‏-rlm",
"–-ndash",
"—-mdash",
"‘-lsquo",
"’-rsquo",
"‚-sbquo",
"“-ldquo",
"”-rdquo",
"„-bdquo",
"†-dagger",
"‡-Dagger",
"•-bull",
"…-hellip",
"‰-permil",
"′-prime",
"″-Prime",
"‹-lsaquo",
"›-rsaquo",
"‾-oline",
"⁄-frasl",
"€-euro",
"ℑ-image",
"℘-weierp",
"ℜ-real",
"™-trade",
"ℵ-alefsym",
"←-larr",
"↑-uarr",
"→-rarr",
"↓-darr",
"↔-harr",
"↵-crarr",
"⇐-lArr",
"⇑-uArr",
"⇒-rArr",
"⇓-dArr",
"⇔-hArr",
"∀-forall",
"∂-part",
"∃-exist",
"∅-empty",
"∇-nabla",
"∈-isin",
"∉-notin",
"∋-ni",
"∏-prod",
"∑-sum",
"−-minus",
"∗-lowast",
"√-radic",
"∝-prop",
"∞-infin",
"∠-ang",
"∧-and",
"∨-or",
"∩-cap",
"∪-cup",
"∫-int",
"∴-there4",
"∼-sim",
"≅-cong",
"≈-asymp",
"≠-ne",
"≡-equiv",
"≤-le",
"≥-ge",
"⊂-sub",
"⊃-sup",
"⊄-nsub",
"⊆-sube",
"⊇-supe",
"⊕-oplus",
"⊗-otimes",
"⊥-perp",
"⋅-sdot",
"⌈-lceil",
"⌉-rceil",
"⌊-lfloor",
"⌋-rfloor",
"〈-lang",
"〉-rang",
"◊-loz",
"♠-spades",
"♣-clubs",
"♥-hearts",
"♦-diams"
};
private static Dictionary<string, char> _lookupTable = WebUtility.HtmlEntities.GenerateLookupTable();
private static Dictionary<string, char> GenerateLookupTable() {
Dictionary<string, char> dictionary = new Dictionary<string, char>(StringComparer.Ordinal);
string[] entitiesList = WebUtility.HtmlEntities._entitiesList;
for (int i = 0; i < entitiesList.Length; i++) {
string text = entitiesList[i];
dictionary.Add(text.Substring(2), text[0]);
}
return dictionary;
}
public static char Lookup(string entity) {
char result;
WebUtility.HtmlEntities._lookupTable.TryGetValue(entity, out result);
return result;
}
}
private enum UnicodeDecodingConformance {
Auto,
Strict,
Compat,
Loose
}
private static char[] _htmlEntityEndingChars = new char[] { ';', '&' };
private static readonly UnicodeDecodingConformance _htmlDecodeConformance = UnicodeDecodingConformance.Auto;
public static string HtmlDecode(string value) {
if (string.IsNullOrEmpty(value)) {
return value;
}
if (!WebUtility.StringRequiresHtmlDecoding(value)) {
return value;
}
StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
WebUtility.HtmlDecode(value, stringWriter);
return stringWriter.ToString();
}
private static bool StringRequiresHtmlDecoding(string s) {
if (WebUtility._htmlDecodeConformance == UnicodeDecodingConformance.Compat) {
return s.IndexOf('&') >= 0;
}
for (int i = 0; i < s.Length; i++) {
char c = s[i];
if (c == '&' || char.IsSurrogate(c)) {
return true;
}
}
return false;
}
private static void ConvertSmpToUtf16(uint smpChar, out char leadingSurrogate, out char trailingSurrogate) {
int num = (int)(smpChar - 65536u);
leadingSurrogate = (char)(num / 1024 + 55296);
trailingSurrogate = (char)(num % 1024 + 56320);
}
public static void HtmlDecode(string value, TextWriter output) {
if (value == null) {
return;
}
if (output == null) {
throw new ArgumentNullException("output");
}
if (!WebUtility.StringRequiresHtmlDecoding(value)) {
output.Write(value);
return;
}
int length = value.Length;
int i = 0;
while (i < length) {
char c = value[i];
if (c != '&') {
goto IL_1B6;
}
int num = value.IndexOfAny(WebUtility._htmlEntityEndingChars, i + 1);
if (num <= 0 || value[num] != ';') {
goto IL_1B6;
}
string text = value.Substring(i + 1, num - i - 1);
if (text.Length > 1 && text[0] == '#') {
uint num2;
bool flag;
if (text[1] == 'x' || text[1] == 'X') {
flag = uint.TryParse(text.Substring(2), NumberStyles.AllowHexSpecifier, NumberFormatInfo.InvariantInfo, out num2);
} else {
flag = uint.TryParse(text.Substring(1), NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out num2);
}
if (flag) {
switch (WebUtility._htmlDecodeConformance) {
case UnicodeDecodingConformance.Strict:
flag = (num2 < 55296u || (57343u < num2 && num2 <= 1114111u));
break;
case UnicodeDecodingConformance.Compat:
flag = (0u < num2 && num2 <= 65535u);
break;
case UnicodeDecodingConformance.Loose:
flag = (num2 <= 1114111u);
break;
default:
flag = false;
break;
}
}
if (!flag) {
goto IL_1B6;
}
if (num2 <= 65535u) {
output.Write((char)num2);
} else {
char value2;
char value3;
WebUtility.ConvertSmpToUtf16(num2, out value2, out value3);
output.Write(value2);
output.Write(value3);
}
i = num;
} else {
i = num;
char c2 = WebUtility.HtmlEntities.Lookup(text);
if (c2 != '\0') {
c = c2;
goto IL_1B6;
}
output.Write('&');
output.Write(text);
output.Write(';');
}
IL_1BD:
i++;
continue;
IL_1B6:
output.Write(c);
goto IL_1BD;
}
}
}

Related

Function to return middle letter or letters of a palindrome

I'm writing a function to test if a string is a palindrome or not and I am wondering how to return the middle letter or letters if the string is indeed a palindrome?
Here's what i have so far:
My bool to check if the string is a palindrome:
public static bool IsPalindrome(string input)
{
int i = 0;
int j = input.Length - 1;
while (true)
{
if (i > j)
{
return true;
}
char a = input[i];
char b = input[j];
if (!a.Equals(b))
{
return false;
}
i++;
j--;
}
}
And here is where I'd like to be able to print out the middle letter(s):
while (true)
{
Console.Clear();
Regex myRegex = new Regex("[ ;:,.-?'!\"]");
string userInput = String.Empty;
Console.WriteLine("Please enter sentence or phrase");
userInput = Console.ReadLine();
Console.WriteLine();
if (IsPalindrome(myRegex.Replace(userInput, string.Empty).ToLower()))
{
Console.WriteLine("True");
Console.WriteLine("Press any key to continue");
}
else
{
Console.WriteLine("False");
Console.WriteLine("Press any key to continue");
}
Console.ReadLine();
}
Here's another implementation to return the middle letter(s):
public string MiddleLettersOf(string s)
{
if (s.Length == 0)
return "";
if ((s.Length & 1) == 1) // Odd length?
return s.Substring(s.Length/2, 1);
return s.Substring(s.Length/2-1, 2);
}
(This assumes that passing a null string is an error and therefore I allow it to throw a NullReferenceException.)
By the way, a simple (but not the most efficient) way of checking a string to see if it's a palindrome is:
public static bool IsPalindrome(string s)
{
return s.SequenceEqual(s.Reverse());
}
You can generalise that test to any IEnumerable:
public static bool IsPalindrome<T>(IEnumerable<T> s)
{
return s.SequenceEqual(s.Reverse());
}
but the flaw with that code is that s is enumerated twice, which might be A Bad Thing.
Try this:
private static string GetMiddleLetters(string input)
{
//Find the middle point
var mid = input.Length / 2.0;
//If it's odd, we take 1 letter, if it's even, we take 2
var numToTake = (mid == (int)mid) ? 2 : 1;
//Round up from the middle, and subtract one (as Substring is 0-indexed)
var startIndex = (int)Math.Ceiling(mid) - 1;
return input.Substring((int)Math.Ceiling(mid) - 1, numToTake);
}
And use it like this:
var fixedString = myRegex.Replace(userInput, string.Empty).ToLower();
if (IsPalindrome(fixedString))
{
Console.WriteLine(GetMiddleLetters(fixedString));
//Rest of the code here...
}
Something like that:
public static String MiddleLetters(string value) {
if (String.IsNullOrEmpty(value))
return value; // middle of the "null" is supposed to be null
return value.Length % 2 == 0 ?
value.Substring(value.Length / 2 - 1, 2)
: value.Substring(value.Length / 2, 1);
}
To test for being palindrome:
public static bool IsPalindrome(string value) {
if (String.IsNullOrEmpty(value))
return true; // or false, or throw an exception
//TODO: are you looking for case sensitive or case insensitive palindromes?
for (int i = 0; i < value.Length / 2; ++i)
if (value[i] != value[value.Length - i - 1])
return false;
return true;
}

Matching strings with wildcard

I would like to match strings with a wildcard (*), where the wildcard means "any". For example:
*X = string must end with X
X* = string must start with X
*X* = string must contain X
Also, some compound uses such as:
*X*YZ* = string contains X and contains YZ
X*YZ*P = string starts with X, contains YZ and ends with P.
Is there a simple algorithm to do this? I'm unsure about using regex (though it is a possibility).
To clarify, the users will type in the above to a filter box (as simple a filter as possible), I don't want them to have to write regular expressions themselves. So something I can easily transform from the above notation would be good.
Often, wild cards operate with two type of jokers:
? - any character (one and only one)
* - any characters (zero or more)
so you can easily convert these rules into appropriate regular expression:
// If you want to implement both "*" and "?"
private static String WildCardToRegular(String value) {
return "^" + Regex.Escape(value).Replace("\\?", ".").Replace("\\*", ".*") + "$";
}
// If you want to implement "*" only
private static String WildCardToRegular(String value) {
return "^" + Regex.Escape(value).Replace("\\*", ".*") + "$";
}
And then you can use Regex as usual:
String test = "Some Data X";
Boolean endsWithEx = Regex.IsMatch(test, WildCardToRegular("*X"));
Boolean startsWithS = Regex.IsMatch(test, WildCardToRegular("S*"));
Boolean containsD = Regex.IsMatch(test, WildCardToRegular("*D*"));
// Starts with S, ends with X, contains "me" and "a" (in that order)
Boolean complex = Regex.IsMatch(test, WildCardToRegular("S*me*a*X"));
You could use the VB.NET Like-Operator:
string text = "x is not the same as X and yz not the same as YZ";
bool contains = LikeOperator.LikeString(text,"*X*YZ*", Microsoft.VisualBasic.CompareMethod.Binary);
Use CompareMethod.Text if you want to ignore the case.
You need to add using Microsoft.VisualBasic.CompilerServices; and add a reference to the Microsoft.VisualBasic.dll.
Since it's part of the .NET framework and will always be, it's not a problem to use this class.
For those using .NET Core 2.1+ or .NET 5+, you can use the FileSystemName.MatchesSimpleExpression method in the System.IO.Enumeration namespace.
string text = "X is a string with ZY in the middle and at the end is P";
bool isMatch = FileSystemName.MatchesSimpleExpression("X*ZY*P", text);
Both parameters are actually ReadOnlySpan<char> but you can use string arguments too. There's also an overloaded method if you want to turn on/off case matching. It is case insensitive by default as Chris mentioned in the comments.
Using of WildcardPattern from System.Management.Automation may be an option.
pattern = new WildcardPattern(patternString);
pattern.IsMatch(stringToMatch);
Visual Studio UI may not allow you to add System.Management.Automation assembly to References of your project. Feel free to add it manually, as described here.
A wildcard * can be translated as .* or .*? regex pattern.
You might need to use a singleline mode to match newline symbols, and in this case, you can use (?s) as part of the regex pattern.
You can set it for the whole or part of the pattern:
X* = > #"X(?s:.*)"
*X = > #"(?s:.*)X"
*X* = > #"(?s).*X.*"
*X*YZ* = > #"(?s).*X.*YZ.*"
X*YZ*P = > #"(?s:X.*YZ.*P)"
*X*YZ* = string contains X and contains YZ
#".*X.*YZ"
X*YZ*P = string starts with X, contains YZ and ends with P.
#"^X.*YZ.*P$"
It is necessary to take into consideration, that Regex IsMatch gives true with XYZ, when checking match with Y*. To avoid it, I use "^" anchor
isMatch(str1, "^" + str2.Replace("*", ".*?"));
So, full code to solve your problem is
bool isMatchStr(string str1, string str2)
{
string s1 = str1.Replace("*", ".*?");
string s2 = str2.Replace("*", ".*?");
bool r1 = Regex.IsMatch(s1, "^" + s2);
bool r2 = Regex.IsMatch(s2, "^" + s1);
return r1 || r2;
}
This is kind of an improvement on the popular answer from #Dmitry Bychenko above (https://stackoverflow.com/a/30300521/4491768). In order to support ? and * as a matching characters we have to escape them. Use \\? or \\* to escape them.
Also a pre compiled regex will improve the performance (on reuse).
public class WildcardPattern
{
private readonly string _expression;
private readonly Regex _regex;
public WildcardPattern(string pattern)
{
if (string.IsNullOrEmpty(pattern)) throw new ArgumentNullException(nameof(pattern));
_expression = "^" + Regex.Escape(pattern)
.Replace("\\\\\\?","??").Replace("\\?", ".").Replace("??","\\?")
.Replace("\\\\\\*","**").Replace("\\*", ".*").Replace("**","\\*") + "$";
_regex = new Regex(_expression, RegexOptions.Compiled);
}
public bool IsMatch(string value)
{
return _regex.IsMatch(value);
}
}
usage
new WildcardPattern("Hello *\\**\\?").IsMatch("Hello W*rld?");
new WildcardPattern(#"Hello *\**\?").IsMatch("Hello W*rld?");
To support those one with C#+Excel (for partial known WS name) but not only - here's my code with wildcard (ddd*).
Briefly: the code gets all WS names and if today's weekday(ddd) matches the first 3 letters of WS name (bool=true) then it turn it to string that gets extracted out of the loop.
using System;
using Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using Range = Microsoft.Office.Interop.Excel.Range;
using System.Diagnostics;
using System.Reflection;
using System.IO;
using System.Text.RegularExpressions;
...
string weekDay = DateTime.Now.ToString("ddd*");
Workbook sourceWorkbook4 = xlApp.Workbooks.Open(LrsIdWorkbook, 0, false, 5, "", "", true, XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
Workbook destinationWorkbook = xlApp.Workbooks.Open(masterWB, 0, false, 5, "", "", true, XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
static String WildCardToRegular(String value)
{
return "^" + Regex.Escape(value).Replace("\\*", ".*") + "$";
}
string wsName = null;
foreach (Worksheet works in sourceWorkbook4.Worksheets)
{
Boolean startsWithddd = Regex.IsMatch(works.Name, WildCardToRegular(weekDay + "*"));
if (startsWithddd == true)
{
wsName = works.Name.ToString();
}
}
Worksheet sourceWorksheet4 = (Worksheet)sourceWorkbook4.Worksheets.get_Item(wsName);
...
public class Wildcard
{
private readonly string _pattern;
public Wildcard(string pattern)
{
_pattern = pattern;
}
public static bool Match(string value, string pattern)
{
int start = -1;
int end = -1;
return Match(value, pattern, ref start, ref end);
}
public static bool Match(string value, string pattern, char[] toLowerTable)
{
int start = -1;
int end = -1;
return Match(value, pattern, ref start, ref end, toLowerTable);
}
public static bool Match(string value, string pattern, ref int start, ref int end)
{
return new Wildcard(pattern).IsMatch(value, ref start, ref end);
}
public static bool Match(string value, string pattern, ref int start, ref int end, char[] toLowerTable)
{
return new Wildcard(pattern).IsMatch(value, ref start, ref end, toLowerTable);
}
public bool IsMatch(string str)
{
int start = -1;
int end = -1;
return IsMatch(str, ref start, ref end);
}
public bool IsMatch(string str, char[] toLowerTable)
{
int start = -1;
int end = -1;
return IsMatch(str, ref start, ref end, toLowerTable);
}
public bool IsMatch(string str, ref int start, ref int end)
{
if (_pattern.Length == 0) return false;
int pindex = 0;
int sindex = 0;
int pattern_len = _pattern.Length;
int str_len = str.Length;
start = -1;
while (true)
{
bool star = false;
if (_pattern[pindex] == '*')
{
star = true;
do
{
pindex++;
}
while (pindex < pattern_len && _pattern[pindex] == '*');
}
end = sindex;
int i;
while (true)
{
int si = 0;
bool breakLoops = false;
for (i = 0; pindex + i < pattern_len && _pattern[pindex + i] != '*'; i++)
{
si = sindex + i;
if (si == str_len)
{
return false;
}
if (str[si] == _pattern[pindex + i])
{
continue;
}
if (si == str_len)
{
return false;
}
if (_pattern[pindex + i] == '?' && str[si] != '.')
{
continue;
}
breakLoops = true;
break;
}
if (breakLoops)
{
if (!star)
{
return false;
}
sindex++;
if (si == str_len)
{
return false;
}
}
else
{
if (start == -1)
{
start = sindex;
}
if (pindex + i < pattern_len && _pattern[pindex + i] == '*')
{
break;
}
if (sindex + i == str_len)
{
if (end <= start)
{
end = str_len;
}
return true;
}
if (i != 0 && _pattern[pindex + i - 1] == '*')
{
return true;
}
if (!star)
{
return false;
}
sindex++;
}
}
sindex += i;
pindex += i;
if (start == -1)
{
start = sindex;
}
}
}
public bool IsMatch(string str, ref int start, ref int end, char[] toLowerTable)
{
if (_pattern.Length == 0) return false;
int pindex = 0;
int sindex = 0;
int pattern_len = _pattern.Length;
int str_len = str.Length;
start = -1;
while (true)
{
bool star = false;
if (_pattern[pindex] == '*')
{
star = true;
do
{
pindex++;
}
while (pindex < pattern_len && _pattern[pindex] == '*');
}
end = sindex;
int i;
while (true)
{
int si = 0;
bool breakLoops = false;
for (i = 0; pindex + i < pattern_len && _pattern[pindex + i] != '*'; i++)
{
si = sindex + i;
if (si == str_len)
{
return false;
}
char c = toLowerTable[str[si]];
if (c == _pattern[pindex + i])
{
continue;
}
if (si == str_len)
{
return false;
}
if (_pattern[pindex + i] == '?' && c != '.')
{
continue;
}
breakLoops = true;
break;
}
if (breakLoops)
{
if (!star)
{
return false;
}
sindex++;
if (si == str_len)
{
return false;
}
}
else
{
if (start == -1)
{
start = sindex;
}
if (pindex + i < pattern_len && _pattern[pindex + i] == '*')
{
break;
}
if (sindex + i == str_len)
{
if (end <= start)
{
end = str_len;
}
return true;
}
if (i != 0 && _pattern[pindex + i - 1] == '*')
{
return true;
}
if (!star)
{
return false;
}
sindex++;
continue;
}
}
sindex += i;
pindex += i;
if (start == -1)
{
start = sindex;
}
}
}
}
C# Console application sample
Command line Sample:
C:/> App_Exe -Opy PythonFile.py 1 2 3
Console output:
Argument list: -Opy PythonFile.py 1 2 3
Found python filename: PythonFile.py
using System;
using System.Text.RegularExpressions; //Regex
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
string cmdLine = String.Join(" ", args);
bool bFileExtFlag = false;
int argIndex = 0;
Regex regex;
foreach (string s in args)
{
//Search for the 1st occurrence of the "*.py" pattern
regex = new Regex(#"(?s:.*)\056py", RegexOptions.IgnoreCase);
bFileExtFlag = regex.IsMatch(s);
if (bFileExtFlag == true)
break;
argIndex++;
};
Console.WriteLine("Argument list: " + cmdLine);
if (bFileExtFlag == true)
Console.WriteLine("Found python filename: " + args[argIndex]);
else
Console.WriteLine("Python file with extension <.py> not found!");
}
}
}

Check if a string is a palindrome

I have a string as input and have to break the string in two substrings. If the left substring equals the right substring then do some logic.
How can I do this?
Sample:
public bool getStatus(string myString)
{
}
Example: myString = "ankYkna", so if we break it into two substring it would be:
left-part = "ank",
right-part = "ank" (after reversal).
Just for fun:
return myString.SequenceEqual(myString.Reverse());
public static bool getStatus(string myString)
{
string first = myString.Substring(0, myString.Length / 2);
char[] arr = myString.ToCharArray();
Array.Reverse(arr);
string temp = new string(arr);
string second = temp.Substring(0, temp.Length / 2);
return first.Equals(second);
}
int length = myString.Length;
for (int i = 0; i < length / 2; i++)
{
if (myString[i] != myString[length - i - 1])
return false;
}
return true;
Using LINQ and off course far from the best solution
var original = "ankYkna";
var reversed = new string(original.Reverse().ToArray());
var palindrom = original == reversed;
A single line of code using Linq
public static bool IsPalindrome(string str)
{
return str.SequenceEqual(str.Reverse());
}
public static bool IsPalindrome(string value)
{
int i = 0;
int j = value.Length - 1;
while (true)
{
if (i > j)
{
return true;
}
char a = value[i];
char b = value[j];
if (char.ToLower(a) != char.ToLower(b))
{
return false;
}
i++;
j--;
}
}
//This c# method will check for even and odd lengh palindrome string
public static bool IsPalenDrome(string palendromeString)
{
bool isPalenDrome = false;
try
{
int halfLength = palendromeString.Length / 2;
string leftHalfString = palendromeString.Substring(0,halfLength);
char[] reversedArray = palendromeString.ToCharArray();
Array.Reverse(reversedArray);
string reversedString = new string(reversedArray);
string rightHalfStringReversed = reversedString.Substring(0, halfLength);
isPalenDrome = leftHalfString == rightHalfStringReversed ? true : false;
}
catch (Exception ex)
{
throw ex;
}
return isPalenDrome;
}
In C# :
public bool EhPalindromo(string text)
{
var reverseText = string.Join("", text.ToLower().Reverse());
return reverseText == text;
}
This is a short and efficient way of checking palindrome.
bool checkPalindrome(string inputString) {
int length = inputString.Length;
for(int i = 0; i < length/2; i++){
if(inputString[i] != inputString[length-1-i]){
return false;
}
}
return true;
}
This way is both concise in appearance & processes very quickly.
Func<string, bool> IsPalindrome = s => s.Reverse().Equals(s);
public static bool IsPalindrome(string word)
{
//first reverse the string
string reversedString = new string(word.Reverse().ToArray());
return string.Compare(word, reversedString) == 0 ? true : false;
}
Out of all the solutions, below can also be tried:
public static bool IsPalindrome(string s)
{
return s == new string(s.Reverse().ToArray());
}
String extension method, easy to use:
public static bool IsPalindrome(this string str)
{
str = new Regex("[^a-zA-Z]").Replace(str, "").ToLower();
return !str.Where((t, i) => t != str[str.Length - i - 1]).Any();
}
private void CheckIfPalindrome(string str)
{
//place string in array of chars
char[] array = str.ToCharArray();
int length = array.Length -1 ;
Boolean palindrome =true;
for (int i = 0; i <= length; i++)//go through the array
{
if (array[i] != array[length])//compare if the char in the same positions are the same eg "tattarrattat" will compare array[0]=t with array[11] =t if are not the same stop the for loop
{
MessageBox.Show("not");
palindrome = false;
break;
}
else //if they are the same make length smaller by one and do the same
{
length--;
}
}
if (palindrome) MessageBox.Show("Palindrome");
}
use this way from dotnetperls
using System;
class Program
{
/// <summary>
/// Determines whether the string is a palindrome.
/// </summary>
public static bool IsPalindrome(string value)
{
int min = 0;
int max = value.Length - 1;
while (true)
{
if (min > max)
{
return true;
}
char a = value[min];
char b = value[max];
// Scan forward for a while invalid.
while (!char.IsLetterOrDigit(a))
{
min++;
if (min > max)
{
return true;
}
a = value[min];
}
// Scan backward for b while invalid.
while (!char.IsLetterOrDigit(b))
{
max--;
if (min > max)
{
return true;
}
b = value[max];
}
if (char.ToLower(a) != char.ToLower(b))
{
return false;
}
min++;
max--;
}
}
static void Main()
{
string[] array =
{
"A man, a plan, a canal: Panama.",
"A Toyota. Race fast, safe car. A Toyota.",
"Cigar? Toss it in a can. It is so tragic.",
"Dammit, I'm mad!",
"Delia saw I was ailed.",
"Desserts, I stressed!",
"Draw, O coward!",
"Lepers repel.",
"Live not on evil.",
"Lonely Tylenol.",
"Murder for a jar of red rum.",
"Never odd or even.",
"No lemon, no melon.",
"Senile felines.",
"So many dynamos!",
"Step on no pets.",
"Was it a car or a cat I saw?",
"Dot Net Perls is not a palindrome.",
"Why are you reading this?",
"This article is not useful.",
"...",
"...Test"
};
foreach (string value in array)
{
Console.WriteLine("{0} = {1}", value, IsPalindrome(value));
}
}
}
If you just need to detect a palindrome, you can do it with a regex, as explained here. Probably not the most efficient approach, though...
That is non-trivial, there is no built in method to do that for you, you'll have to write your own. You will need to consider what rules you would like to check, like you implicitly stated you accepted reversing of one string. Also, you missed out the middle character, is this only if odd length?
So you will have something like:
if(myString.length % 2 = 0)
{
//even
string a = myString.substring(0, myString.length / 2);
string b = myString.substring(myString.length / 2 + 1, myString.lenght/2);
if(a == b)
return true;
//Rule 1: reverse
if(a == b.reverse()) //can't remember if this is a method, if not you'll have to write that too
return true;
etc, also doing whatever you want for odd strings
This C# method will check for even and odd length palindrome string (Recursive Approach):
public static bool IsPalindromeResursive(int rightIndex, int leftIndex, char[] inputString)
{
if (rightIndex == leftIndex || rightIndex < leftIndex)
return true;
if (inputString[rightIndex] == inputString[leftIndex])
return IsPalindromeResursive(--rightIndex, ++leftIndex, inputString);
else
return false;
}
public Boolean IsPalindrome(string value)
{
var one = value.ToList<char>();
var two = one.Reverse<char>().ToList();
return one.Equals(two);
}
class Program
{
static void Main(string[] args)
{
string s, revs = "";
Console.WriteLine(" Enter string");
s = Console.ReadLine();
for (int i = s.Length - 1; i >= 0; i--) //String Reverse
{
Console.WriteLine(i);
revs += s[i].ToString();
}
if (revs == s) // Checking whether string is palindrome or not
{
Console.WriteLine("String is Palindrome");
}
else
{
Console.WriteLine("String is not Palindrome");
}
Console.ReadKey();
}
}
public bool IsPalindroom(string input)
{
input = input.ToLower();
var loops = input.Length / 2;
var higherBoundIdx = input.Length - 1;
for (var lowerBoundIdx = 0; lowerBoundIdx < loops; lowerBoundIdx++, higherBoundIdx--)
{
if (input[lowerBoundIdx] != input[higherBoundIdx])
return false;
}
return true;
}
Here is an absolutely simple way to do this,
Receive the word as input into a method.
Assign a temp variable to the original value.
Loop through the initial word, and add the last character to the reversal that you are constructing until the inital word has no more characters.
Now use the spare you created to hold the original value to compare to the constructed copy.
This is a nice way as u don't have to cast ints and doubles. U can just pass them to the method in their string representation by using the ToString() method.
public static bool IsPalindrome(string word)
{
string spare = word;
string reversal = null;
while (word.Length > 0)
{
reversal = string.Concat(reversal, word.LastOrDefault());
word = word.Remove(word.Length - 1);
}
return spare.Equals(reversal);
}
So from your main method,
For even and odd length strings u just pass the whole string into the method.
Since a palindrome also includes numbers, words, sentences, and any combinations of these, and should ignore punctuation and case, (See Wikipedia Article)
I propose this solution:
public class Palindrome
{
static IList<int> Allowed = new List<int> {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'1', '2', '3', '4', '5', '6', '7', '8', '9',
'0'
};
private static int[] GetJustAllowed(string text)
{
List<int> characters = new List<int>();
foreach (var c in text)
characters.Add(c | 0x20);
return characters.Where(c => Allowed.Contains(c)).ToArray();
}
public static bool IsPalindrome(string text)
{
if(text == null || text.Length == 1)
return true;
int[] chars = GetJustAllowed(text);
var length = chars.Length;
while (length > 0)
if (chars[chars.Length - length] != chars[--length])
return false;
return true;
}
public static bool IsPalindrome(int number)
{
return IsPalindrome(number.ToString());
}
public static bool IsPalindrome(double number)
{
return IsPalindrome(number.ToString());
}
public static bool IsPalindrome(decimal number)
{
return IsPalindrome(number.ToString());
}
}
static void Main(string[] args)
{
string str, rev="";
Console.Write("Enter string");
str = Console.ReadLine();
for (int i = str.Length - 1; i >= 0; i--)
{
rev = rev + str[i];
}
if (rev == str)
Console.Write("Entered string is pallindrome");
else
Console.Write("Entered string is not pallindrome");
Console.ReadKey();
}
string test = "Malayalam";
char[] palindrome = test.ToCharArray();
char[] reversestring = new char[palindrome.Count()];
for (int i = palindrome.Count() - 1; i >= 0; i--)
{
reversestring[palindrome.Count() - 1 - i] = palindrome[i];
}
string materializedString = new string(reversestring);
if (materializedString.ToLower() == test.ToLower())
{
Console.Write("Palindrome!");
}
else
{
Console.Write("Not a Palindrome!");
}
Console.Read();
public static bool palindrome(string t)
{
int i = t.Length;
for (int j = 0; j < i / 2; j++)
{
if (t[j] == t[i - j-1])
{
continue;
}
else
{
return false;
break;
}
}
return true;
}
public bool Solution(string content)
{
int length = content.Length;
int half = length/2;
int isOddLength = length%2;
// Counter for checking the string from the middle
int j = (isOddLength==0) ? half:half+1;
for(int i=half-1;i>=0;i--)
{
if(content[i] != content[j])
{
return false;
}
j++;
}
return true;
}
public bool MojTestPalindrome (string word)
{
bool yes = false;
char[]test1 = word.ToArray();
char[] test2 = test1.Reverse().ToArray();
for (int i=0; i< test2.Length; i++)
{
if (test1[i] != test2[test2.Length - 1 - i])
{
yes = false;
break;
}
else {
yes = true;
}
}
if (yes == true)
{
return true;
}
else
return false;
}
public static bool IsPalindrome(string str)
{
int i = 0;
int a = 0;
char[] chr = str.ToCharArray();
foreach (char cr in chr)
{
Array.Reverse(chr);
if (chr[i] == cr)
{
if (a == str.Length)
{
return true;
}
a++;
i++;
}
else
{
return false;
}
}
return true;
}
The various provided answers are wrong for numerous reasons, primarily from misunderstanding what a palindrome is. The majority only properly identify a subset of palindromes.
From Merriam-Webster
A word, verse, or sentence (such as "Able was I ere I saw Elba")
And from Wordnik
A word, phrase, verse, or sentence that reads the same backward or forward. For example: A man, a plan, a canal, Panama!
Consider non-trivial palindromes such as "Malayalam" (it's a proper language, so naming rules apply, and it should be capitalized), or palindromic sentences such as "Was it a car or a cat I saw?" or "No 'X' in Nixon".
These are recognized palindromes in any literature.
I'm lifting the thorough solution from a library providing this kind of stuff that I'm the primary author of, so the solution works for both String and ReadOnlySpan<Char> because that's a requirement I've imposed on the library. The solution for purely String will be easy to determine from this, however.
public static Boolean IsPalindrome(this String #string) =>
!(#string is null) && #string.AsSpan().IsPalindrome();
public static Boolean IsPalindrome(this ReadOnlySpan<Char> span) {
// First we need to build the string without any punctuation or whitespace or any other
// unrelated-to-reading characters.
StringBuilder builder = new StringBuilder(span.Length);
foreach (Char s in span) {
if (!(s.IsControl()
|| s.IsPunctuation()
|| s.IsSeparator()
|| s.IsWhiteSpace()) {
_ = builder.Append(s);
}
}
String prepped = builder.ToString();
String reversed = prepped.Reverse().Join();
// Now actually check it's a palindrome
return String.Equals(prepped, reversed, StringComparison.CurrentCultureIgnoreCase);
}
You're going to want variants of this that accept a CultureInfo parameter as well, when you're testing a specific language rather than your own language, by instead calling .ToUpper(cultureInfo) on prepped.
And here's proof from the projects unit tests that it works.

How to separate character and number part from string

E.g., I would like to separate:
OS234 to OS and 234
AA4230 to AA and 4230
I have used following trivial solution, but I am quite sure that there should be a more efficient and robust solution .
private void demo()
{ string cell="ABCD4321";
int a = getIndexofNumber(cell);
string Numberpart = cell.Substring(a, cell.Length - a);
row = Convert.ToInt32(rowpart);
string Stringpart = cell.Substring(0, a);
}
private int getIndexofNumber(string cell)
{
int a = -1, indexofNum = 10000;
a = cell.IndexOf("0"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
a = cell.IndexOf("1"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
a = cell.IndexOf("2"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
a = cell.IndexOf("3"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
a = cell.IndexOf("4"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
a = cell.IndexOf("5"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
a = cell.IndexOf("6"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
a = cell.IndexOf("7"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
a = cell.IndexOf("8"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
a = cell.IndexOf("9"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
if (indexofNum != 10000)
{ return indexofNum; }
else
{ return 0; }
}
Regular Expressions are best suited for this kind of work:
using System.Text.RegularExpressions;
Regex re = new Regex(#"([a-zA-Z]+)(\d+)");
Match result = re.Match(input);
string alphaPart = result.Groups[1].Value;
string numberPart = result.Groups[2].Value;
Use Linq to do this
string str = "OS234";
var digits = from c in str
select c
where Char.IsDigit(c);
var alphas = from c in str
select c
where !Char.IsDigit(c);
Everyone and their mother will give you a solution using regex, so here's one that is not:
// s is string of form ([A-Za-z])*([0-9])* ; char added
int index = s.IndexOfAny(new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' });
string chars = s.Substring(0, index);
int num = Int32.Parse(s.Substring(index));
I really like jason's answer. Lets improve it a bit. We dont need regex here. My solution handle input like "H1N1":
public static IEnumerable<string> SplitAlpha(string input)
{
var words = new List<string> { string.Empty };
for (var i = 0; i < input.Length; i++)
{
words[words.Count-1] += input[i];
if (i + 1 < input.Length && char.IsLetter(input[i]) != char.IsLetter(input[i + 1]))
{
words.Add(string.Empty);
}
}
return words;
}
This solution is linear O(n).
output
"H1N1" -> ["H", "1", "N", "1"]
"H" -> ["H"]
"GH1N12" -> ["GH", "1", "N", "12"]
"OS234" -> ["OS", "234"]
Same solution with a StringBuilder
public static IEnumerable<string> SplitAlpha(string input)
{
var words = new List<StringBuilder>{new StringBuilder()};
for (var i = 0; i < input.Length; i++)
{
words[words.Count - 1].Append(input[i]);
if (i + 1 < input.Length && char.IsLetter(input[i]) != char.IsLetter(input[i + 1]))
{
words.Add(new StringBuilder());
}
}
return words.Select(x => x.ToString());
}
Try it Online!
If you want resolve more occurrences of char followed by number or vice versa you can use
private string SplitCharsAndNums(string text)
{
var sb = new StringBuilder();
for (var i = 0; i < text.Length - 1; i++)
{
if ((char.IsLetter(text[i]) && char.IsDigit(text[i+1])) ||
(char.IsDigit(text[i]) && char.IsLetter(text[i+1])))
{
sb.Append(text[i]);
sb.Append(" ");
}
else
{
sb.Append(text[i]);
}
}
sb.Append(text[text.Length-1]);
return sb.ToString();
}
And then
var text = SplitCharsAndNums("asd1 asas4gr5 6ssfd");
var tokens = text.Split(' ');
Are you doing this for sorting purposes? If so, keep in mind that Regex can kill performance for large lists. I frequently use an AlphanumComparer that's a general solution to this problem (can handle any sequence of letters and numbers in any order). I believe that I adapted it from this page.
Even if you're not sorting on it, using the character-by-character approach (if you have variable lengths) or simple substring/parse (if they're fixed) will be a lot more efficient and easier to test than a Regex.
I have used bniwredyc's answer to get Improved version of my routine:
private void demo()
{
string cell = "ABCD4321";
int row, a = getIndexofNumber(cell);
string Numberpart = cell.Substring(a, cell.Length - a);
row = Convert.ToInt32(Numberpart);
string Stringpart = cell.Substring(0, a);
}
private int getIndexofNumber(string cell)
{
int indexofNum=-1;
foreach (char c in cell)
{
indexofNum++;
if (Char.IsDigit(c))
{
return indexofNum;
}
}
return indexofNum;
}
.NET 2.0 compatible, without regex
public class Result
{
private string _StringPart;
public string StringPart
{
get { return _StringPart; }
}
private int _IntPart;
public int IntPart
{
get { return _IntPart; }
}
public Result(string stringPart, int intPart)
{
_StringPart = stringPart;
_IntPart = intPart;
}
}
class Program
{
public static Result GetResult(string source)
{
string stringPart = String.Empty;
int intPart;
var buffer = new StringBuilder();
foreach (char c in source)
{
if (Char.IsDigit(c))
{
if (stringPart == String.Empty)
{
stringPart = buffer.ToString();
buffer.Remove(0, buffer.Length);
}
}
buffer.Append(c);
}
if (!int.TryParse(buffer.ToString(), out intPart))
{
return null;
}
return new Result(stringPart, intPart);
}
static void Main(string[] args)
{
Result result = GetResult("OS234");
Console.WriteLine("String part: {0} int part: {1}", result.StringPart, result.IntPart);
result = GetResult("AA4230 ");
Console.WriteLine("String part: {0} int part: {1}", result.StringPart, result.IntPart);
result = GetResult("ABCD4321");
Console.WriteLine("String part: {0} int part: {1}", result.StringPart, result.IntPart);
Console.ReadKey();
}
}
Just use the substring function and set position inside the bracket.
String id = "DON123";
System.out.println("Id nubmer is : "+id.substring(3,6));
Answer:
Id number is: 123
use Split to seprate string from sting that use tab \t and space
string s = "sometext\tsometext\tsometext";
string[] split = s.Split('\t');
now you have an array of string that you want too easy

What JSON library works well for you in .NET? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 6 years ago.
Improve this question
I'd be interested in hearing what JSON library folks in the community have been using inside of .NET? I have a need to parse/serialize some JSON object graphs from inside .NET (C#) to actual .NET types. I could roll my own, but if there are some solid libraries folks have used, I'd like to hear your comments. I saw the list of libraries on the json.org site, but it's a fairly big list and the community is usually good at vetting out the contenders from the pretenders
Any details (pros/cons) of your experience with the library would be incredibly helpful. -- thanks in advance.
I've used Json.NET with success in the past.
Example from the site:
Product product = new Product();
product.Name = "Apple";
product.Expiry = new DateTime(2008, 12, 28);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };
string json = JsonConvert.SerializeObject(product);
//{
// "Name": "Apple",
// "Expiry": new Date(1230422400000),
// "Price": 3.99,
// "Sizes": [
// "Small",
// "Medium",
// "Large"
// ]
//}
Product deserializedProduct = JsonConvert.DeserializeObject<Product>(json);
There are at least two built into the framework.
The newer : System.Runtime.Serialization.Json
and the older : System.Web.Script.Serialization
I prefer to not have dependencies on 3rd party libraries. I work with JSON every day and have never needed anything more than what already exists in the framework.
I wrote my own JSON serializer using DataContractJsonSerializer in the System.ServiceModel.Web.dll assembly [which is a component of WCF included in .NET 3.5 as a standard assembly, and in the .NET 3.5 SP1 Client Profile] (in .NET 4.0 and Silverlight 4, it's been moved to System.Runtime.Serialization.dll).
using System.IO;
using System.Runtime.Serialization.Json;
public class JsonObjectSerializer
{
public string Serialize<T>(T instance) where T : class
{
var serializer = new DataContractJsonSerializer(typeof(T));
using (var memoryStream = new MemoryStream())
{
serializer.WriteObject(memoryStream, instance);
memoryStream.Flush();
memoryStream.Position = 0;
using (var reader = new StreamReader(memoryStream))
{
return reader.ReadToEnd();
}
}
}
public T Deserialize<T>(string serialized) where T : class
{
var serializer = new DataContractJsonSerializer(typeof(T));
using (var memoryStream = new MemoryStream())
{
using (var writer = new StreamWriter(memoryStream))
{
writer.Write(serialized);
writer.Flush();
memoryStream.Position = 0;
return serializer.ReadObject(memoryStream) as T;
}
}
}
}
You should also try my ServiceStack JsonSerializer - it's the fastest .NET JSON serializer at the moment based on the benchmarks of the leading JSON serializers and supports serializing any POCO Type, DataContracts, Lists/Dictionaries, Interfaces, Inheritance, Late-bound objects including anonymous types, etc.
Basic Example:
Customer customer = new Customer { Name="Joe Bloggs", Age=31 };
string json = customer.ToJson();
Customer fromJson = json.FromJson<Customer>(json);
Check out the System.Runtime.Serialization.Json Namespace included with .NET 3.5.
I typed "json" into google and the top hit was json.org, which leads to what looks like a good single utility class:
using System;
using System.Collections;
using System.Globalization;
using System.Text;
namespace Procurios.Public
{
/// <summary>
/// This class encodes and decodes JSON strings.
/// Spec. details, see http://www.json.org/
///
/// JSON uses Arrays and Objects. These correspond here to the datatypes ArrayList and Hashtable.
/// All numbers are parsed to doubles.
/// </summary>
public class JSON
{
public const int TOKEN_NONE = 0;
public const int TOKEN_CURLY_OPEN = 1;
public const int TOKEN_CURLY_CLOSE = 2;
public const int TOKEN_SQUARED_OPEN = 3;
public const int TOKEN_SQUARED_CLOSE = 4;
public const int TOKEN_COLON = 5;
public const int TOKEN_COMMA = 6;
public const int TOKEN_STRING = 7;
public const int TOKEN_NUMBER = 8;
public const int TOKEN_TRUE = 9;
public const int TOKEN_FALSE = 10;
public const int TOKEN_NULL = 11;
private const int BUILDER_CAPACITY = 2000;
/// <summary>
/// Parses the string json into a value
/// </summary>
/// <param name="json">A JSON string.</param>
/// <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>
public static object JsonDecode(string json)
{
bool success = true;
return JsonDecode(json, ref success);
}
/// <summary>
/// Parses the string json into a value; and fills 'success' with the successfullness of the parse.
/// </summary>
/// <param name="json">A JSON string.</param>
/// <param name="success">Successful parse?</param>
/// <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>
public static object JsonDecode(string json, ref bool success)
{
success = true;
if (json != null) {
char[] charArray = json.ToCharArray();
int index = 0;
object value = ParseValue(charArray, ref index, ref success);
return value;
} else {
return null;
}
}
/// <summary>
/// Converts a Hashtable / ArrayList object into a JSON string
/// </summary>
/// <param name="json">A Hashtable / ArrayList</param>
/// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
public static string JsonEncode(object json)
{
StringBuilder builder = new StringBuilder(BUILDER_CAPACITY);
bool success = SerializeValue(json, builder);
return (success ? builder.ToString() : null);
}
protected static Hashtable ParseObject(char[] json, ref int index, ref bool success)
{
Hashtable table = new Hashtable();
int token;
// {
NextToken(json, ref index);
bool done = false;
while (!done) {
token = LookAhead(json, index);
if (token == JSON.TOKEN_NONE) {
success = false;
return null;
} else if (token == JSON.TOKEN_COMMA) {
NextToken(json, ref index);
} else if (token == JSON.TOKEN_CURLY_CLOSE) {
NextToken(json, ref index);
return table;
} else {
// name
string name = ParseString(json, ref index, ref success);
if (!success) {
success = false;
return null;
}
// :
token = NextToken(json, ref index);
if (token != JSON.TOKEN_COLON) {
success = false;
return null;
}
// value
object value = ParseValue(json, ref index, ref success);
if (!success) {
success = false;
return null;
}
table[name] = value;
}
}
return table;
}
protected static ArrayList ParseArray(char[] json, ref int index, ref bool success)
{
ArrayList array = new ArrayList();
// [
NextToken(json, ref index);
bool done = false;
while (!done) {
int token = LookAhead(json, index);
if (token == JSON.TOKEN_NONE) {
success = false;
return null;
} else if (token == JSON.TOKEN_COMMA) {
NextToken(json, ref index);
} else if (token == JSON.TOKEN_SQUARED_CLOSE) {
NextToken(json, ref index);
break;
} else {
object value = ParseValue(json, ref index, ref success);
if (!success) {
return null;
}
array.Add(value);
}
}
return array;
}
protected static object ParseValue(char[] json, ref int index, ref bool success)
{
switch (LookAhead(json, index)) {
case JSON.TOKEN_STRING:
return ParseString(json, ref index, ref success);
case JSON.TOKEN_NUMBER:
return ParseNumber(json, ref index, ref success);
case JSON.TOKEN_CURLY_OPEN:
return ParseObject(json, ref index, ref success);
case JSON.TOKEN_SQUARED_OPEN:
return ParseArray(json, ref index, ref success);
case JSON.TOKEN_TRUE:
NextToken(json, ref index);
return true;
case JSON.TOKEN_FALSE:
NextToken(json, ref index);
return false;
case JSON.TOKEN_NULL:
NextToken(json, ref index);
return null;
case JSON.TOKEN_NONE:
break;
}
success = false;
return null;
}
protected static string ParseString(char[] json, ref int index, ref bool success)
{
StringBuilder s = new StringBuilder(BUILDER_CAPACITY);
char c;
EatWhitespace(json, ref index);
// "
c = json[index++];
bool complete = false;
while (!complete) {
if (index == json.Length) {
break;
}
c = json[index++];
if (c == '"') {
complete = true;
break;
} else if (c == '\\') {
if (index == json.Length) {
break;
}
c = json[index++];
if (c == '"') {
s.Append('"');
} else if (c == '\\') {
s.Append('\\');
} else if (c == '/') {
s.Append('/');
} else if (c == 'b') {
s.Append('\b');
} else if (c == 'f') {
s.Append('\f');
} else if (c == 'n') {
s.Append('\n');
} else if (c == 'r') {
s.Append('\r');
} else if (c == 't') {
s.Append('\t');
} else if (c == 'u') {
int remainingLength = json.Length - index;
if (remainingLength >= 4) {
// parse the 32 bit hex into an integer codepoint
uint codePoint;
if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint))) {
return "";
}
// convert the integer codepoint to a unicode char and add to string
s.Append(Char.ConvertFromUtf32((int)codePoint));
// skip 4 chars
index += 4;
} else {
break;
}
}
} else {
s.Append(c);
}
}
if (!complete) {
success = false;
return null;
}
return s.ToString();
}
protected static double ParseNumber(char[] json, ref int index, ref bool success)
{
EatWhitespace(json, ref index);
int lastIndex = GetLastIndexOfNumber(json, index);
int charLength = (lastIndex - index) + 1;
double number;
success = Double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number);
index = lastIndex + 1;
return number;
}
protected static int GetLastIndexOfNumber(char[] json, int index)
{
int lastIndex;
for (lastIndex = index; lastIndex < json.Length; lastIndex++) {
if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) {
break;
}
}
return lastIndex - 1;
}
protected static void EatWhitespace(char[] json, ref int index)
{
for (; index < json.Length; index++) {
if (" \t\n\r".IndexOf(json[index]) == -1) {
break;
}
}
}
protected static int LookAhead(char[] json, int index)
{
int saveIndex = index;
return NextToken(json, ref saveIndex);
}
protected static int NextToken(char[] json, ref int index)
{
EatWhitespace(json, ref index);
if (index == json.Length) {
return JSON.TOKEN_NONE;
}
char c = json[index];
index++;
switch (c) {
case '{':
return JSON.TOKEN_CURLY_OPEN;
case '}':
return JSON.TOKEN_CURLY_CLOSE;
case '[':
return JSON.TOKEN_SQUARED_OPEN;
case ']':
return JSON.TOKEN_SQUARED_CLOSE;
case ',':
return JSON.TOKEN_COMMA;
case '"':
return JSON.TOKEN_STRING;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '-':
return JSON.TOKEN_NUMBER;
case ':':
return JSON.TOKEN_COLON;
}
index--;
int remainingLength = json.Length - index;
// false
if (remainingLength >= 5) {
if (json[index] == 'f' &&
json[index + 1] == 'a' &&
json[index + 2] == 'l' &&
json[index + 3] == 's' &&
json[index + 4] == 'e') {
index += 5;
return JSON.TOKEN_FALSE;
}
}
// true
if (remainingLength >= 4) {
if (json[index] == 't' &&
json[index + 1] == 'r' &&
json[index + 2] == 'u' &&
json[index + 3] == 'e') {
index += 4;
return JSON.TOKEN_TRUE;
}
}
// null
if (remainingLength >= 4) {
if (json[index] == 'n' &&
json[index + 1] == 'u' &&
json[index + 2] == 'l' &&
json[index + 3] == 'l') {
index += 4;
return JSON.TOKEN_NULL;
}
}
return JSON.TOKEN_NONE;
}
protected static bool SerializeValue(object value, StringBuilder builder)
{
bool success = true;
if (value is string) {
success = SerializeString((string)value, builder);
} else if (value is Hashtable) {
success = SerializeObject((Hashtable)value, builder);
} else if (value is ArrayList) {
success = SerializeArray((ArrayList)value, builder);
} else if (IsNumeric(value)) {
success = SerializeNumber(Convert.ToDouble(value), builder);
} else if ((value is Boolean) && ((Boolean)value == true)) {
builder.Append("true");
} else if ((value is Boolean) && ((Boolean)value == false)) {
builder.Append("false");
} else if (value == null) {
builder.Append("null");
} else {
success = false;
}
return success;
}
protected static bool SerializeObject(Hashtable anObject, StringBuilder builder)
{
builder.Append("{");
IDictionaryEnumerator e = anObject.GetEnumerator();
bool first = true;
while (e.MoveNext()) {
string key = e.Key.ToString();
object value = e.Value;
if (!first) {
builder.Append(", ");
}
SerializeString(key, builder);
builder.Append(":");
if (!SerializeValue(value, builder)) {
return false;
}
first = false;
}
builder.Append("}");
return true;
}
protected static bool SerializeArray(ArrayList anArray, StringBuilder builder)
{
builder.Append("[");
bool first = true;
for (int i = 0; i < anArray.Count; i++) {
object value = anArray[i];
if (!first) {
builder.Append(", ");
}
if (!SerializeValue(value, builder)) {
return false;
}
first = false;
}
builder.Append("]");
return true;
}
protected static bool SerializeString(string aString, StringBuilder builder)
{
builder.Append("\"");
char[] charArray = aString.ToCharArray();
for (int i = 0; i < charArray.Length; i++) {
char c = charArray[i];
if (c == '"') {
builder.Append("\\\"");
} else if (c == '\\') {
builder.Append("\\\\");
} else if (c == '\b') {
builder.Append("\\b");
} else if (c == '\f') {
builder.Append("\\f");
} else if (c == '\n') {
builder.Append("\\n");
} else if (c == '\r') {
builder.Append("\\r");
} else if (c == '\t') {
builder.Append("\\t");
} else {
int codepoint = Convert.ToInt32(c);
if ((codepoint >= 32) && (codepoint <= 126)) {
builder.Append(c);
} else {
builder.Append("\\u" + Convert.ToString(codepoint, 16).PadLeft(4, '0'));
}
}
}
builder.Append("\"");
return true;
}
protected static bool SerializeNumber(double number, StringBuilder builder)
{
builder.Append(Convert.ToString(number, CultureInfo.InvariantCulture));
return true;
}
/// <summary>
/// Determines if a given object is numeric in any way
/// (can be integer, double, null, etc).
///
/// Thanks to mtighe for pointing out Double.TryParse to me.
/// </summary>
protected static bool IsNumeric(object o)
{
double result;
return (o == null) ? false : Double.TryParse(o.ToString(), out result);
}
}
}
I've used System.Web.Helpers.Json for encoding and decoding. It decodes into a dynamic types, which is a good match for javascript's dynamic data.

Categories