Switch statement skips cases and goes to default - c#

I have a switch statement that assigns a value to an array of two dimensions depending on the case. The problem is that sometimes the cases are skipped and the default case is called. There seems to be no pattern to the functioning of the switch (sometimes works and sometimes goes to default).
I have tried changing the type of the value that I am giving to the switch from float to string because, in other questions that I saw before, the problem was the data type.
I also checked that the value that I was giving to the switch was one of the cases and I debug.log the "code"(variable of the value that I am giving to my switch) and it is always one of the cases.
I even changed the switch statement to a chain of if-else statements to see if I wrote the switch wrong but that didn't help.
The following code is a function that is generating the "code" that is given to the switch function that goes to default.
private void NodeEnumeration()
{
//for each node in _mapArray
for(int i = 0; i < _mapSize; i++)
{
for(int e = 0; e < _mapSize; e++)
{
//if node value is not 0
if(_mapArray[i, e] != 0)
{
float code = 0f;
//Left
if(e != 0)
{
_surroundingNodesPositions[0].x = e - 1;
_surroundingNodesPositions[0].y = i;
if(_mapArray[(int)_surroundingNodesPositions[0].y, (int)_surroundingNodesPositions[0].x] > 0)
{
code += 1f;
}
}
//Up
if(i != 0)
{
_surroundingNodesPositions[1].x = e;
_surroundingNodesPositions[1].y = i - 1;
if(_mapArray[(int)_surroundingNodesPositions[1].y, (int)_surroundingNodesPositions[1].x] > 0)
{
code += 0.1f;
}
}
//Right
if(e != _mapSize - 1)
{
_surroundingNodesPositions[2].x = e + 1;
_surroundingNodesPositions[2].y = i;
if(_mapArray[(int)_surroundingNodesPositions[2].y, (int)_surroundingNodesPositions[2].x] > 0)
{
code += 0.01f;
}
}
//Down
if(i != _mapSize - 1)
{
_surroundingNodesPositions[3].x = e;
_surroundingNodesPositions[3].y = i + 1;
if(_mapArray[(int)_surroundingNodesPositions[3].y, (int)_surroundingNodesPositions[3].x] > 0)
{
code += 0.001f;
}
}
AssignValue(i, e, code);//! Here is the problem
}
}
}
}
private void AssignValue(int i, int e, float code)
{
switch(code)
{
case 1.111f:
_mapArray[i, e] = 1;
break;
case 1.110f:
_mapArray[i, e] = 2;
break;
case 1.101f:
_mapArray[i, e] = 3;
break;
case 1.100f:
_mapArray[i, e] = 4;
break;
case 1.011f:
_mapArray[i, e] = 5;
break;
case 1.010f:
_mapArray[i, e] = 6;
break;
case 1.001f:
_mapArray[i, e] = 7;
break;
case 1.000f:
_mapArray[i, e] = 8;
break;
case 0.111f:
_mapArray[i, e] = 9;
break;
case 0.110f:
_mapArray[i, e] = 10;
break;
case 0.101f:
_mapArray[i, e] = 11;
break;
case 0.100f:
_mapArray[i, e] = 12;
break;
case 0.011f:
_mapArray[i, e] = 13;
break;
case 0.010f:
_mapArray[i, e] = 14;
break;
case 0.001f:
_mapArray[i, e] = 15;
break;
case 0.000f:
_mapArray[i, e] = 16;
break;
default:
Debug.LogError("MapGenerator::NodeEnumeration()::AssignValue() no value can be assigned | code: " + code);
Debug.Log("height: " + i + " width: " + e);
break;
}
}

The problem that you're dealing with is a result of floating point (im)precision. Read through the answer here, as it explains it a lot better that I could do.
TLDR: you can't rely on decimal math to be precise, even simple equations such as 0.1f + 0.2f != 0.3f. In most cases, you should just use integer math, or epsilons for judging if two floating point values are equal:
private static float EPSILON = 0.0005f;
public static bool AreApproximatelyEqual(float a, float b)
{
return Mathf.Abs(a - b) < EPSILON;
}
However, in your case specifically, we can do a LOT better than just using floating point epsilons, or even integer math. Using boolean math is the best way to go about this, with a concept called "flags".
Notice how the final value assigned into the _mapArray is always equal to 16 - value (when removing the decimal point, and converting the value to binary). Example: 1.111f = 0b1111 in binary, = 15 in decimal. 16-15 = 1, your intended value.
Since this holds true for all of your cases, we can set each bit within the using the OR operation:
0100
|= 0010
---------
0110
The final implementation then would look something like this:
private void NodeEnumeration()
{
//for each node in _mapArray
for(int i = 0; i < _mapSize; i++)
{
for(int e = 0; e < _mapSize; e++)
{
//if node value is not 0
if(_mapArray[i, e] != 0)
{
int code = 0; //change our code to be an integer. we could go all the way down to a byte, if you wanted.
//Left
if(e != 0)
{
_surroundingNodesPositions[0].x = e - 1;
_surroundingNodesPositions[0].y = i;
if(_mapArray[(int)_surroundingNodesPositions[0].y, (int)_surroundingNodesPositions[0].x] > 0)
{
code |= 0b1000; //set the 4th bit in this case. Equivalent to += 8
}
}
//Up
if(i != 0)
{
_surroundingNodesPositions[1].x = e;
_surroundingNodesPositions[1].y = i - 1;
if(_mapArray[(int)_surroundingNodesPositions[1].y, (int)_surroundingNodesPositions[1].x] > 0)
{
code |= 0b0100; //set the 3rd bit in this case. Equivalent to += 4
}
}
//Right
if(e != _mapSize - 1)
{
_surroundingNodesPositions[2].x = e + 1;
_surroundingNodesPositions[2].y = i;
if(_mapArray[(int)_surroundingNodesPositions[2].y, (int)_surroundingNodesPositions[2].x] > 0)
{
code |= 0b0010; //set the 2nd bit in this case. Equivalent to += 2
}
}
//Down
if(i != _mapSize - 1)
{
_surroundingNodesPositions[3].x = e;
_surroundingNodesPositions[3].y = i + 1;
if(_mapArray[(int)_surroundingNodesPositions[3].y, (int)_surroundingNodesPositions[3].x] > 0)
{
code |= 0b0001; //Set the 1st bit in this case. Equivalent to += 1
}
}
_mapArray[i, e] = 16 - flags;
}
}
}
}

Related

How to optimize and do this more readeable

Hi everyone I have a question about how to make this more clean, reusable and readable. I have some data models (relics) and this has rarity and level. Depending on the level and the rarity it has to be grouped to later apply this in some math calculation. For this reason, I need to parse all my relics and check for the level and rarity and store in a var counter.
public double TotalGlobalBonus
{
get
{
float commonRarityMultiplier = 20;
float rareRarityMultiplier = 120;
float epicRarityMultiplier = 320;
float legendaryRarityMultiplier = 540;
int rarityCommonLevel1 = 0;
int rarityCommonLevel2 = 0;
int rarityCommonLevel3 = 0;
int rarityCommonLevel4 = 0;
int rarityCommonLevel5 = 0;
int rarityRareLevel1 = 0;
int rarityRareLevel2 = 0;
int rarityRareLevel3 = 0;
int rarityRareLevel4 = 0;
int rarityRareLevel5 = 0;
int rarityEpicLevel1 = 0;
int rarityEpicLevel2 = 0;
int rarityEpicLevel3 = 0;
int rarityEpicLevel4 = 0;
int rarityEpicLevel5 = 0;
int rarityLegendaryLevel1 = 0;
int rarityLegendaryLevel2 = 0;
int rarityLegendaryLevel3 = 0;
int rarityLegendaryLevel4 = 0;
int rarityLegendaryLevel5 = 0;
foreach (RelicModel relic in this.equipedRelics)
{
switch (relic.rarity)
{
case RarityType.COMMON:
switch (relic.Level)
{
case 1:
rarityCommonLevel1++;
break;
case 2:
rarityCommonLevel2++;
break;
case 3:
rarityCommonLevel3++;
break;
case 4:
rarityCommonLevel4++;
break;
case 5:
rarityCommonLevel5++;
break;
}
break;
case RarityType.RARE:
switch (relic.Level)
{
case 1:
rarityRareLevel1++;
break;
case 2:
rarityRareLevel2++;
break;
case 3:
rarityRareLevel3++;
break;
case 4:
rarityRareLevel4++;
break;
case 5:
rarityRareLevel5++;
break;
}
break;
case RarityType.EPIC:
switch (relic.Level)
{
case 1:
rarityEpicLevel1++;
break;
case 2:
rarityEpicLevel2++;
break;
case 3:
rarityEpicLevel3++;
break;
case 4:
rarityEpicLevel4++;
break;
case 5:
rarityEpicLevel5++;
break;
}
break;
case RarityType.LEGENDARY:
{
switch (relic.Level)
{
case 1:
rarityLegendaryLevel1++;
break;
case 2:
rarityLegendaryLevel2++;
break;
case 3:
rarityLegendaryLevel3++;
break;
case 4:
rarityLegendaryLevel4++;
break;
case 5:
rarityLegendaryLevel5++;
break;
}
break;
}
}
}
double common = (commonRarityMultiplier / 100) * (rarityCommonLevel1 * 1 + rarityCommonLevel2 * 5 +
rarityCommonLevel3 * 10 + rarityCommonLevel4 * 20 +
rarityCommonLevel5 * 40);
double rare = (rareRarityMultiplier / 100) * (rarityRareLevel1 * 1 + rarityRareLevel2 * 5 +
rarityRareLevel3 * 10 + rarityRareLevel4 * 20 +
rarityRareLevel5 * 40);
double epic = (epicRarityMultiplier / 100) * (rarityEpicLevel1 * 1 + rarityEpicLevel2 * 5 +
rarityEpicLevel3 * 10 + rarityEpicLevel4 * 20 +
rarityEpicLevel5 * 40);
double legendary = (legendaryRarityMultiplier / 100) * (rarityLegendaryLevel1 * 1 + rarityLegendaryLevel2 * 5 +
rarityLegendaryLevel3 * 10 + rarityLegendaryLevel4 * 20 +
rarityLegendaryLevel5 * 40);
double final = common + rare + epic + legendary;
return final;
}
}
It is a really longe property and will grow if the number of levels grows, so this is not precisely scalable
You need to put all these values in a Dictionary with keys of type RarityType and values of type int[].
var rarity = new Dictionary<RarityType, int[]>();
rarity[RarityType.COMMON] = new int[6];
rarity[RarityType.RARE] = new int[6];
rarity[RarityType.EPIC] = new int[6];
rarity[RarityType.LEGENDARY] = new int[6];
foreach (RelicModel relic in this.equipedRelics)
{
rarity[relic.rarity][relic.Level]++;
}
The first element of each array, the one having index = 0, is intended to be unused.

What is the meaning of \uE0001.\uE000 in the decompiled source?

I'm using this proximity card scanner software to read my card for testing. They provide an SDK but it covers everything except desktop scanners, even though their software is able to read it fine. I noticed their software was coded in .NET and I was able to decompile it using JetBrains dotPeek. I'm very close to what I'm needing. I was able to fix the decompiled code except for these 3 lines. I'm unfamiliar with how to handle the encoding:
string[] array = ((IEnumerable<string>) ConfigurationManager.AppSettings.AllKeys).Where<string>((Func<string, bool>) (stg => stg.StartsWith(\uE0001.\uE000(27359)))).ToArray<string>();
string str = ((IEnumerable<string>) ConfigurationManager.AppSettings.AllKeys).Any<string>((Func<string, bool>) (stg => stg == \uE0001.\uE000(28615))) ? ConfigurationManager.AppSettings[\uE0001.\uE000(28615)] : directoryName;
assyTypeKey = index2.Replace(\uE0001.\uE000(28428), \uE0001.\uE000(28439));
I know they're Unicode characters but it's looking very foreign to me with that declaration next to it in parentheses. I'm not sure how to handle this.
Here's one of the functions. I know it's ugly but it's decompiled:
public static void LoadSubServices()
{
//SubServicesManager._logger.Info((object) \uE0001.\uE000(28243));
string[] array = ((IEnumerable<string>) ConfigurationManager.AppSettings.AllKeys).Where<string>((Func<string, bool>) (stg => stg.StartsWith(\uE0001.\uE000(27359)))).ToArray<string>();
label_2:
int num1 = 1;
while (true)
{
switch (num1)
{
case 0:
//SubServicesManager._logger.Error((object) \uE0001.\uE000(28270));
num1 = 2;
continue;
case 1:
if (array.Length == 0)
{
num1 = 0;
continue;
}
goto label_7;
case 2:
goto label_31;
default:
goto label_2;
}
}
label_7:
string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
//SubServicesManager._logger.InfoFormat(\uE0001.\uE000(28582), (object) directoryName);
string str = ((IEnumerable<string>) ConfigurationManager.AppSettings.AllKeys).Any<string>((Func<string, bool>) (stg => stg == \uE0001.\uE000(28615))) ? ConfigurationManager.AppSettings[\uE0001.\uE000(28615)] : directoryName;
label_9:
int num2 = 1;
while (true)
{
switch (num2)
{
case 0:
//SubServicesManager._logger.ErrorFormat(\uE0001.\uE000(28621), (object) str);
num2 = 2;
continue;
case 1:
if (!Directory.Exists(str))
{
num2 = 0;
continue;
}
goto label_14;
case 2:
goto label_6;
default:
goto label_9;
}
}
label_6:
return;
label_14:
label_33:
for (int index1 = 0; index1 < array.Length; ++index1)
{
label_17:
int num3 = 2;
string appSetting1 = null;
string appSetting2 = null;
ILoadableServer loadableServer = null;
string index2 = null;
string assyTypeKey = null;
while (true)
{
switch (num3)
{
case 0:
//SubServicesManager._logger.InfoFormat(\uE0001.\uE000(28419), (object) appSetting1);
num3 = 1;
continue;
case 1:
loadableServer = SubServicesManager.LoadSubServiceAssembly(appSetting1, str, appSetting2);
num3 = 5;
continue;
case 2:
index2 = array[index1];
num3 = 3;
continue;
case 3:
assyTypeKey = index2.Replace(\uE0001.\uE000(28428), \uE0001.\uE000(28439));
num3 = 6;
continue;
case 4:
//SubServicesManager._logger.ErrorFormat(\uE0001.\uE000(28442), (object) appSetting1);
num3 = 9;
continue;
case 5:
if (loadableServer == null)
{
num3 = 4;
continue;
}
goto label_29;
case 6:
appSetting2 = ConfigurationManager.AppSettings[index2];
num3 = 7;
continue;
case 7:
if (((IEnumerable<string>) ConfigurationManager.AppSettings.AllKeys).Any<string>((Func<string, bool>) (stg => stg == assyTypeKey)))
{
num3 = 8;
continue;
}
goto label_30;
case 8:
appSetting1 = ConfigurationManager.AppSettings[assyTypeKey];
num3 = 0;
continue;
case 9:
goto label_33;
default:
goto label_17;
}
}
label_29:
SubServicesManager._loadableServices.Add(loadableServer);
//SubServicesManager._logger.InfoFormat(\uE0001.\uE000(28496));
continue;
label_30:
return;
//SubServicesManager._logger.WarnFormat(\uE0001.\uE000(28525), (object) index2, (object) appSetting2);
}
return;
label_31:;
}

How to reliably send large strings via serial port to arduino

I'm trying to send a large string to Arduino as constant as possible, But whatever i try it is never reliable, On c# I have:
private void OnTimedEvent(Object myObject, EventArgs myEventArgs)
{
string start = "0:" + 255 * (l / 100) + ":0;";
start = string.Concat(Enumerable.Repeat(start, 24));
start += ".";
char[] end = start.ToCharArray();
port.Write(new string(end));
Invalidate();
}
And on arduino i have:
#include <FTRGBLED.h>
#include <Vect3d.h>
int clockPin = 13;
int dataPin = 11;
Tinker::Vect3d<float> recCol(0,0,0);
RGBLEDChain led(25, clockPin, dataPin);
int count = 0, ledc = 0;
void setup() {
led.begin();
Serial.begin(19200);
}
void loop() {
if(Serial.available() > 0){
char c = Serial.read();
if(c == '.')
{
ledc = 0;
recCol[0] = 0;
recCol[1] = 0;
recCol[2] = 0;
led.update();
}
else
{
if(c == ';')
{
FTLEDColour col = { recCol[0] , recCol[1] , recCol[2] };
led.setLED(ledc, col);
recCol[0] = 0;
recCol[1] = 0;
recCol[2] = 0;
ledc++;
count = 0;
}
if(c == ':')
{
count++;
}
else
{
switch(count){
case 0:
recCol[0] = recCol[0] * 10 + (c - '0');
break;
case 1:
recCol[1] = recCol[1] * 10 + (c - '0');
break;
case 2:
recCol[2] = recCol[1] * 10 + (c - '0');
break;
}
}
}
}
}
When the string is created fully static in c# all the lights change, When I try to add some non static bits such as the 255 * var, it suddenly only works for the first LED and flashes random colours, not greenfrom 0 - 255.
First of all, have you confirmed the string being sent is as you expect it to be?
I would start by writing it to debug.writeline to verify.
Second, if you use the Arduino terminal and type the string to it, does it respond as expected?

Smallest Multiple

I have a code here written in C# that finds the smallest multiple by all numbers from 1 to 20. However, I find it very inefficient since the execution took awhile before producing the correct answer. I would like to know what are the different ways that I can do to improve the code. Thank You.
public static void SmallestMultiple()
{
const ushort ARRAY_SIZE = 21;
ushort[] array = new ushort[ARRAY_SIZE];
ushort check = 0;
for (uint value = 1; value < uint.MaxValue; value++)
{
for (ushort j = 1; j < ARRAY_SIZE; j++)
{
array[j] = j;
if (value % array[j] == 0)
{
check++;
}
}
if (check == 20)
{
Console.WriteLine("The value is {0}", value);
}
else
{
check = 0;
}
}
}
static void Main(string[] args)
{
int[] nums = Enumerable.Range(1, 20).ToArray();
int lcm = 1;
for (int i = 0; i < nums.Length; i++)
{
lcm = LCM(lcm, nums[i]);
}
Console.WriteLine("LCM = {0}", lcm);
}
public static int LCM(int value1, int value2)
{
int a = Math.Abs(value1);
int b = Math.Abs(value2);
// perform division first to avoid potential overflow
a = checked((a / GCD(a, b)));
return checked((a * b));
}
public static int GCD(int value1, int value2)
{
int gcd = 1; // Greates Common Divisor
// throw exception if any value=0
if (value1 == 0 || value2 == 0)
{
throw new ArgumentOutOfRangeException();
}
// assign absolute values to local vars
int a = Math.Abs(value1); // local var1
int b = Math.Abs(value2); // local var2
// if numbers are equal return the first
if (a == b) { return a; }
// if var "b" is GCD return "b"
if (a > b && a % b == 0) { return b; }
// if var "a" is GCD return "a"
if (b > a && b % a == 0) { return a; }
// Euclid algorithm to find GCD (a,b):
// estimated maximum iterations:
// 5* (number of dec digits in smallest number)
while (b != 0)
{
gcd = b;
b = a % b;
a = gcd;
}
return gcd;
}
}
Source : Fast Integer Algorithms: Greatest Common Divisor and Least Common Multiple, .NET solution
Since the result must also be divisible by 19 (which is the greatest prime number) up to 20, you might only cycle through multiples of 19.
This should get to to the result about 19 times faster.
Here's the code that does this:
public static void SmallestMultiple()
{
const ushort ARRAY_SIZE = 21;
ushort[] array = new ushort[ARRAY_SIZE];
ushort check = 0;
for (uint value = 19; value < uint.MaxValue; value += 19)
{
for (ushort j = 1; j < ARRAY_SIZE; j++)
{
array[j] = j;
if (value % array[j] == 0)
{
check++;
}
}
if (check == 20)
{
Console.WriteLine("The value is {0}", value);
return;
}
else
{
check = 0;
}
}
}
On my machine, this finds the result 232792560 in a little over 2 seconds.
Update
Also, please note that the initial program did not stop when reaching a solution; I have added a return statement to make it stop.
You're just looking for the LCM of the numbers from 1 to 20:
Where the GCD can be efficiently calculated with the Euclidean algorithm.
I don't know C#, but this Python solution shouldn't be hard to translate:
def gcd(a, b):
while b != 0:
a, b = b, a % b
return a
def lcm(a, b):
return (a * b) / gcd(a, b)
numbers = range(1, 20 + 1)
print reduce(numbers, lcm)
It's pretty fast too:
>>> %timeit reduce(lcm, range(1, 20000))
1 loops, best of 3: 258 ms per loop
EDIT: v2.0 - Major speed improvement
Building on w0lf's solution. A faster solution:
public static void SmallestMultiple()
{
// this is a bit quick and dirty
// (not too difficult to change to generate primeProduct dynamically for any range)
int primeProduct = 2*3*5*7*11*13*17*19;
for (int value = primeProduct; ; value += primeProduct)
{
bool success = true;
for (int j = 11; j < 21; j++)
{
if (value % j != 0)
{
success = false;
break;
}
}
if (success)
{
Console.WriteLine("The value is {0}", value);
break;
}
}
}
You needn't check 1-10 since if something is divisible by x (e.g. 12), it is divisible by x/n (e.g. 12/2 = 6). The smallest multiple will always be a multiple of a product of all the primes involved.
Didn't benchmark C# solution, but equivalent Java solution runs in about 0.0000006 seconds.
Well I'm not sure what exactly you are trying to accomplish here but your out side for loop will run approximately 4,294,967,295 time (uint.MaxValue). So that will take some time...
If you have a way to keep from going to uint.MaxValue - like breaking your loop when you have accomplished what you need to - that will speed it up.
Also, since you are setting array[j] equal to j and then apparently never using the array again why not just do:
value % j
instead of
value % array[j]
Using also code written by W0lf (sorry but i cannot comment on your post) I would improve it (only a little) deleting the array that I think is useless..
public static void SmallestMultiple()
{
const ushort ARRAY_SIZE = 21;
ushort check = 0;
for (uint value = 1; value < uint.MaxValue; value++)
{
for (ushort j = 1; j < ARRAY_SIZE; j++)
{
if (value % j == 0)
{
check++;
}
}
if (check == 20)
{
Console.WriteLine("The value is {0}", value);
}
else
{
check = 0;
}
}
}

How to escape JSON string?

Are there any classes/functions available to be used for easy JSON escaping? I'd rather not have to write my own.
I use System.Web.HttpUtility.JavaScriptStringEncode
string quoted = HttpUtility.JavaScriptStringEncode(input);
For those using the very popular Json.Net project from Newtonsoft the task is trivial:
using Newtonsoft.Json;
....
var s = JsonConvert.ToString(#"a\b");
Console.WriteLine(s);
....
This code prints:
"a\\b"
That is, the resulting string value contains the quotes as well as the escaped backslash.
Building on the answer by Dejan, what you can do is import System.Web.Helpers .NET Framework assembly, then use the following function:
static string EscapeForJson(string s) {
string quoted = System.Web.Helpers.Json.Encode(s);
return quoted.Substring(1, quoted.Length - 2);
}
The Substring call is required, since Encode automatically surrounds strings with double quotes.
Yep, just add the following function to your Utils class or something:
public static string cleanForJSON(string s)
{
if (s == null || s.Length == 0) {
return "";
}
char c = '\0';
int i;
int len = s.Length;
StringBuilder sb = new StringBuilder(len + 4);
String t;
for (i = 0; i < len; i += 1) {
c = s[i];
switch (c) {
case '\\':
case '"':
sb.Append('\\');
sb.Append(c);
break;
case '/':
sb.Append('\\');
sb.Append(c);
break;
case '\b':
sb.Append("\\b");
break;
case '\t':
sb.Append("\\t");
break;
case '\n':
sb.Append("\\n");
break;
case '\f':
sb.Append("\\f");
break;
case '\r':
sb.Append("\\r");
break;
default:
if (c < ' ') {
t = "000" + String.Format("X", c);
sb.Append("\\u" + t.Substring(t.Length - 4));
} else {
sb.Append(c);
}
break;
}
}
return sb.ToString();
}
I have used following code to escape the string value for json.
You need to add your '"' to the output of the following code:
public static string EscapeStringValue(string value)
{
const char BACK_SLASH = '\\';
const char SLASH = '/';
const char DBL_QUOTE = '"';
var output = new StringBuilder(value.Length);
foreach (char c in value)
{
switch (c)
{
case SLASH:
output.AppendFormat("{0}{1}", BACK_SLASH, SLASH);
break;
case BACK_SLASH:
output.AppendFormat("{0}{0}", BACK_SLASH);
break;
case DBL_QUOTE:
output.AppendFormat("{0}{1}",BACK_SLASH,DBL_QUOTE);
break;
default:
output.Append(c);
break;
}
}
return output.ToString();
}
In .Net Core 3+ and .Net 5+:
string escapedJsonString = JsonEncodedText.Encode(jsonString);
The methods offered here are faulty.
Why venture that far when you could just use System.Web.HttpUtility.JavaScriptEncode ?
If you're on a lower framework, you can just copy paste it from mono
Courtesy of the mono-project #
https://github.com/mono/mono/blob/master/mcs/class/System.Web/System.Web/HttpUtility.cs
public static string JavaScriptStringEncode(string value, bool addDoubleQuotes)
{
if (string.IsNullOrEmpty(value))
return addDoubleQuotes ? "\"\"" : string.Empty;
int len = value.Length;
bool needEncode = false;
char c;
for (int i = 0; i < len; i++)
{
c = value[i];
if (c >= 0 && c <= 31 || c == 34 || c == 39 || c == 60 || c == 62 || c == 92)
{
needEncode = true;
break;
}
}
if (!needEncode)
return addDoubleQuotes ? "\"" + value + "\"" : value;
var sb = new System.Text.StringBuilder();
if (addDoubleQuotes)
sb.Append('"');
for (int i = 0; i < len; i++)
{
c = value[i];
if (c >= 0 && c <= 7 || c == 11 || c >= 14 && c <= 31 || c == 39 || c == 60 || c == 62)
sb.AppendFormat("\\u{0:x4}", (int)c);
else switch ((int)c)
{
case 8:
sb.Append("\\b");
break;
case 9:
sb.Append("\\t");
break;
case 10:
sb.Append("\\n");
break;
case 12:
sb.Append("\\f");
break;
case 13:
sb.Append("\\r");
break;
case 34:
sb.Append("\\\"");
break;
case 92:
sb.Append("\\\\");
break;
default:
sb.Append(c);
break;
}
}
if (addDoubleQuotes)
sb.Append('"');
return sb.ToString();
}
This can be compacted into
// https://github.com/mono/mono/blob/master/mcs/class/System.Json/System.Json/JsonValue.cs
public class SimpleJSON
{
private static bool NeedEscape(string src, int i)
{
char c = src[i];
return c < 32 || c == '"' || c == '\\'
// Broken lead surrogate
|| (c >= '\uD800' && c <= '\uDBFF' &&
(i == src.Length - 1 || src[i + 1] < '\uDC00' || src[i + 1] > '\uDFFF'))
// Broken tail surrogate
|| (c >= '\uDC00' && c <= '\uDFFF' &&
(i == 0 || src[i - 1] < '\uD800' || src[i - 1] > '\uDBFF'))
// To produce valid JavaScript
|| c == '\u2028' || c == '\u2029'
// Escape "</" for <script> tags
|| (c == '/' && i > 0 && src[i - 1] == '<');
}
public static string EscapeString(string src)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
int start = 0;
for (int i = 0; i < src.Length; i++)
if (NeedEscape(src, i))
{
sb.Append(src, start, i - start);
switch (src[i])
{
case '\b': sb.Append("\\b"); break;
case '\f': sb.Append("\\f"); break;
case '\n': sb.Append("\\n"); break;
case '\r': sb.Append("\\r"); break;
case '\t': sb.Append("\\t"); break;
case '\"': sb.Append("\\\""); break;
case '\\': sb.Append("\\\\"); break;
case '/': sb.Append("\\/"); break;
default:
sb.Append("\\u");
sb.Append(((int)src[i]).ToString("x04"));
break;
}
start = i + 1;
}
sb.Append(src, start, src.Length - start);
return sb.ToString();
}
}
I ran speed tests on some of these answers for a long string and a short string. Clive Paterson's code won by a good bit, presumably because the others are taking into account serialization options. Here are my results:
Apple Banana
System.Web.HttpUtility.JavaScriptStringEncode: 140ms
System.Web.Helpers.Json.Encode: 326ms
Newtonsoft.Json.JsonConvert.ToString: 230ms
Clive Paterson: 108ms
\\some\long\path\with\lots\of\things\to\escape\some\long\path\t\with\lots\of\n\things\to\escape\some\long\path\with\lots\of\"things\to\escape\some\long\path\with\lots"\of\things\to\escape
System.Web.HttpUtility.JavaScriptStringEncode: 2849ms
System.Web.Helpers.Json.Encode: 3300ms
Newtonsoft.Json.JsonConvert.ToString: 2827ms
Clive Paterson: 1173ms
And here is the test code:
public static void Main(string[] args)
{
var testStr1 = "Apple Banana";
var testStr2 = #"\\some\long\path\with\lots\of\things\to\escape\some\long\path\t\with\lots\of\n\things\to\escape\some\long\path\with\lots\of\""things\to\escape\some\long\path\with\lots""\of\things\to\escape";
foreach (var testStr in new[] { testStr1, testStr2 })
{
var results = new Dictionary<string,List<long>>();
for (var n = 0; n < 10; n++)
{
var count = 1000 * 1000;
var sw = Stopwatch.StartNew();
for (var i = 0; i < count; i++)
{
var s = System.Web.HttpUtility.JavaScriptStringEncode(testStr);
}
var t = sw.ElapsedMilliseconds;
results.GetOrCreate("System.Web.HttpUtility.JavaScriptStringEncode").Add(t);
sw = Stopwatch.StartNew();
for (var i = 0; i < count; i++)
{
var s = System.Web.Helpers.Json.Encode(testStr);
}
t = sw.ElapsedMilliseconds;
results.GetOrCreate("System.Web.Helpers.Json.Encode").Add(t);
sw = Stopwatch.StartNew();
for (var i = 0; i < count; i++)
{
var s = Newtonsoft.Json.JsonConvert.ToString(testStr);
}
t = sw.ElapsedMilliseconds;
results.GetOrCreate("Newtonsoft.Json.JsonConvert.ToString").Add(t);
sw = Stopwatch.StartNew();
for (var i = 0; i < count; i++)
{
var s = cleanForJSON(testStr);
}
t = sw.ElapsedMilliseconds;
results.GetOrCreate("Clive Paterson").Add(t);
}
Console.WriteLine(testStr);
foreach (var result in results)
{
Console.WriteLine(result.Key + ": " + Math.Round(result.Value.Skip(1).Average()) + "ms");
}
Console.WriteLine();
}
Console.ReadLine();
}
I would also recommend using the JSON.NET library mentioned, but if you have to escape unicode characters (e.g. \uXXXX format) in the resulting JSON string, you may have to do it yourself. Take a look at Converting Unicode strings to escaped ascii string for an example.
I nice one-liner, used JsonConvert as others have but added substring to remove the added quotes and backslash.
var escapedJsonString = JsonConvert.ToString(JsonString).Substring(1, JsonString.Length - 2);
What about System.Web.Helpers.Json.Encode(...) (see http://msdn.microsoft.com/en-us/library/system.web.helpers.json.encode(v=vs.111).aspx)?
String.Format("X", c);
That just outputs: X
Try this instead:
string t = ((int)c).ToString("X");
sb.Append("\\u" + t.PadLeft(4, '0'));
There's a Json library at Codeplex
I chose to use System.Web.Script.Serialization.JavaScriptSerializer.
I have a small static helper class defined as follows:
internal static partial class Serialization
{
static JavaScriptSerializer serializer;
static Serialization()
{
serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = Int32.MaxValue;
}
public static string ToJSON<T>(T obj)
{
return serializer.Serialize(obj);
}
public static T FromJSON<T>(string data)
{
if (Common.IsEmpty(data))
return default(T);
else
return serializer.Deserialize<T>(data);
}
}
To serialize anything I just call Serialization.ToJSON(itemToSerialize)
To deserialize I just call Serialization.FromJSON<T>(jsonValueOfTypeT)

Categories