-
-
Save laloutre87/93bb3b50f91e48b3bc392dc59753c951 to your computer and use it in GitHub Desktop.
C# php Serializer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Serializer Class. | |
/// </summary> | |
public class Serializer | |
{ | |
//types: | |
// N = null | |
// s = string | |
// i = int | |
// d = double | |
// a = array (hashtable) | |
private Dictionary<Hashtable, bool> seenHashtables; //for serialize (to infinte prevent loops) | |
private Dictionary<ArrayList, bool> seenArrayLists; //for serialize (to infinte prevent loops) lol | |
private int pos; //for unserialize | |
public bool XMLSafe = true; //This member tells the serializer wether or not to strip carriage returns from strings when serializing and adding them back in when deserializing | |
//http://www.w3.org/TR/REC-xml/#sec-line-ends | |
public Encoding StringEncoding = new System.Text.UTF8Encoding(); | |
private System.Globalization.NumberFormatInfo nfi; | |
public Serializer() | |
{ | |
this.nfi = new System.Globalization.NumberFormatInfo(); | |
this.nfi.NumberGroupSeparator = ""; | |
this.nfi.NumberDecimalSeparator = "."; | |
} | |
public string Serialize(object obj) | |
{ | |
this.seenArrayLists = new Dictionary<ArrayList, bool>(); | |
this.seenHashtables = new Dictionary<Hashtable, bool>(); | |
return this.serialize(obj, new StringBuilder()).ToString(); | |
}//Serialize(object obj) | |
private StringBuilder serialize(object obj, StringBuilder sb) | |
{ | |
if (obj == null) | |
{ | |
return sb.Append("N;"); | |
} | |
else if (obj is string) | |
{ | |
string str = (string)obj; | |
if (this.XMLSafe) | |
{ | |
str = str.Replace("\r\n", "\n");//replace \r\n with \n | |
str = str.Replace("\r", "\n");//replace \r not followed by \n with a single \n Should we do this? | |
} | |
return sb.Append("s:" + this.StringEncoding.GetByteCount(str) + ":\"" + str + "\";"); | |
} | |
else if (obj is bool) | |
{ | |
return sb.Append("b:" + (((bool)obj) ? "1" : "0") + ";"); | |
} | |
else if (obj is int) | |
{ | |
int i = (int)obj; | |
return sb.Append("i:" + i.ToString(this.nfi) + ";"); | |
} | |
else if (obj is double) | |
{ | |
double d = (double)obj; | |
return sb.Append("d:" + d.ToString(this.nfi) + ";"); | |
} | |
else if (obj is ArrayList) | |
{ | |
if (this.seenArrayLists.ContainsKey((ArrayList)obj)) | |
return sb.Append("N;");//cycle detected | |
else | |
this.seenArrayLists.Add((ArrayList)obj, true); | |
ArrayList a = (ArrayList)obj; | |
sb.Append("a:" + a.Count + ":{"); | |
for (int i = 0; i < a.Count; i++) | |
{ | |
this.serialize(i, sb); | |
this.serialize(a[i], sb); | |
} | |
sb.Append("}"); | |
return sb; | |
} | |
else if (obj is Hashtable) | |
{ | |
if (this.seenHashtables.ContainsKey((Hashtable)obj)) | |
return sb.Append("N;");//cycle detected | |
else | |
this.seenHashtables.Add((Hashtable)obj, true); | |
Hashtable a = (Hashtable)obj; | |
sb.Append("a:" + a.Count + ":{"); | |
foreach (DictionaryEntry entry in a) | |
{ | |
this.serialize(entry.Key, sb); | |
this.serialize(entry.Value, sb); | |
} | |
sb.Append("}"); | |
return sb; | |
} | |
else | |
{ | |
return sb; | |
} | |
}//Serialize(object obj) | |
public object Deserialize(string str) | |
{ | |
this.pos = 0; | |
return deserialize(str); | |
}//Deserialize(string str) | |
private object deserialize(string str) | |
{ | |
if (str == null || str.Length <= this.pos) | |
return new Object(); | |
int start, end, length; | |
string stLen; | |
switch (str[this.pos]) | |
{ | |
case 'N': | |
this.pos += 2; | |
return null; | |
case 'b': | |
char chBool; | |
chBool = str[pos + 2]; | |
this.pos += 4; | |
return chBool == '1'; | |
case 'i': | |
string stInt; | |
start = str.IndexOf(':', this.pos) + 1; | |
end = str.IndexOf(';', start); | |
stInt = str.Substring(start, end - start); | |
this.pos += 3 + stInt.Length; | |
return Int32.Parse(stInt, this.nfi); | |
case 'd': | |
string stDouble; | |
start = str.IndexOf(':', this.pos) + 1; | |
end = str.IndexOf(';', start); | |
stDouble = str.Substring(start, end - start); | |
this.pos += 3 + stDouble.Length; | |
return Double.Parse(stDouble, this.nfi); | |
case 's': | |
start = str.IndexOf(':', this.pos) + 1; | |
end = str.IndexOf(':', start); | |
stLen = str.Substring(start, end - start); | |
int bytelen = Int32.Parse(stLen); | |
length = bytelen; | |
//This is the byte length, not the character length - so we migth | |
//need to shorten it before usage. This also implies bounds checking | |
if ((end + 2 + length) >= str.Length) length = str.Length - 2 - end; | |
string stRet = str.Substring(end + 2, length); | |
while (this.StringEncoding.GetByteCount(stRet) > bytelen) | |
{ | |
length--; | |
stRet = str.Substring(end + 2, length); | |
} | |
this.pos += 6 + stLen.Length + length; | |
if (this.XMLSafe) | |
{ | |
stRet = stRet.Replace("\n", "\r\n"); | |
} | |
return stRet; | |
case 'a': | |
//if keys are ints 0 through N, returns an ArrayList, else returns Hashtable | |
start = str.IndexOf(':', this.pos) + 1; | |
end = str.IndexOf(':', start); | |
stLen = str.Substring(start, end - start); | |
length = Int32.Parse(stLen); | |
Hashtable htRet = new Hashtable(length); | |
ArrayList alRet = new ArrayList(length); | |
this.pos += 4 + stLen.Length; //a:Len:{ | |
for (int i = 0; i < length; i++) | |
{ | |
//read key | |
object key = deserialize(str); | |
//read value | |
object val = deserialize(str); | |
if (alRet != null) | |
{ | |
if (key is int && (int)key == alRet.Count) | |
alRet.Add(val); | |
else | |
alRet = null; | |
} | |
htRet[key] = val; | |
} | |
this.pos++; //skip the } | |
if (this.pos < str.Length && str[this.pos] == ';')//skipping our old extra array semi-colon bug (er... php's weirdness) | |
this.pos++; | |
if (alRet != null) | |
return alRet; | |
else | |
return htRet; | |
default: | |
return ""; | |
}//switch | |
}//unserialzie(object) | |
}//class Serializer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Serializer Class. | |
/// </summary> | |
public class Serializer | |
{ | |
//types: | |
// N = null | |
// s = string | |
// i = int | |
// d = double | |
// a = array (hashtable) | |
private Dictionary<Hashtable, bool> seenHashtables; //for serialize (to infinte prevent loops) | |
private Dictionary<ArrayList, bool> seenArrayLists; //for serialize (to infinte prevent loops) lol | |
private int pos; //for unserialize | |
public bool XMLSafe = true; //This member tells the serializer wether or not to strip carriage returns from strings when serializing and adding them back in when deserializing | |
//http://www.w3.org/TR/REC-xml/#sec-line-ends | |
public Encoding StringEncoding = new System.Text.UTF8Encoding(); | |
private System.Globalization.NumberFormatInfo nfi; | |
public Serializer() | |
{ | |
this.nfi = new System.Globalization.NumberFormatInfo(); | |
this.nfi.NumberGroupSeparator = ""; | |
this.nfi.NumberDecimalSeparator = "."; | |
} | |
public string Serialize(object obj) | |
{ | |
this.seenArrayLists = new Dictionary<ArrayList, bool>(); | |
this.seenHashtables = new Dictionary<Hashtable, bool>(); | |
return this.serialize(obj, new StringBuilder()).ToString(); | |
}//Serialize(object obj) | |
private StringBuilder serialize(object obj, StringBuilder sb) | |
{ | |
if (obj == null) | |
{ | |
return sb.Append("N;"); | |
} | |
else if (obj is string) | |
{ | |
string str = (string)obj; | |
if (this.XMLSafe) | |
{ | |
str = str.Replace("\r\n", "\n");//replace \r\n with \n | |
str = str.Replace("\r", "\n");//replace \r not followed by \n with a single \n Should we do this? | |
} | |
return sb.Append("s:" + this.StringEncoding.GetByteCount(str) + ":\"" + str + "\";"); | |
} | |
else if (obj is bool) | |
{ | |
return sb.Append("b:" + (((bool)obj) ? "1" : "0") + ";"); | |
} | |
else if (obj is int) | |
{ | |
int i = (int)obj; | |
return sb.Append("i:" + i.ToString(this.nfi) + ";"); | |
} | |
else if (obj is double) | |
{ | |
double d = (double)obj; | |
return sb.Append("d:" + d.ToString(this.nfi) + ";"); | |
} | |
else if (obj is ArrayList) | |
{ | |
if (this.seenArrayLists.ContainsKey((ArrayList)obj)) | |
return sb.Append("N;");//cycle detected | |
else | |
this.seenArrayLists.Add((ArrayList)obj, true); | |
ArrayList a = (ArrayList)obj; | |
sb.Append("a:" + a.Count + ":{"); | |
for (int i = 0; i < a.Count; i++) | |
{ | |
this.serialize(i, sb); | |
this.serialize(a[i], sb); | |
} | |
sb.Append("}"); | |
return sb; | |
} | |
else if (obj is Hashtable) | |
{ | |
if (this.seenHashtables.ContainsKey((Hashtable)obj)) | |
return sb.Append("N;");//cycle detected | |
else | |
this.seenHashtables.Add((Hashtable)obj, true); | |
Hashtable a = (Hashtable)obj; | |
sb.Append("a:" + a.Count + ":{"); | |
foreach (DictionaryEntry entry in a) | |
{ | |
this.serialize(entry.Key, sb); | |
this.serialize(entry.Value, sb); | |
} | |
sb.Append("}"); | |
return sb; | |
} | |
else | |
{ | |
return sb; | |
} | |
}//Serialize(object obj) | |
public object Deserialize(string str) | |
{ | |
this.pos = 0; | |
return deserialize(str); | |
}//Deserialize(string str) | |
private object deserialize(string str) | |
{ | |
if (str == null || str.Length <= this.pos) | |
return new Object(); | |
int start, end, length; | |
string stLen; | |
switch (str[this.pos]) | |
{ | |
case 'N': | |
this.pos += 2; | |
return null; | |
case 'b': | |
char chBool; | |
chBool = str[pos + 2]; | |
this.pos += 4; | |
return chBool == '1'; | |
case 'i': | |
string stInt; | |
start = str.IndexOf(":", this.pos) + 1; | |
end = str.IndexOf(";", start); | |
stInt = str.Substring(start, end - start); | |
this.pos += 3 + stInt.Length; | |
return Int32.Parse(stInt, this.nfi); | |
case 'd': | |
string stDouble; | |
start = str.IndexOf(":", this.pos) + 1; | |
end = str.IndexOf(";", start); | |
stDouble = str.Substring(start, end - start); | |
this.pos += 3 + stDouble.Length; | |
return Double.Parse(stDouble, this.nfi); | |
case 's': | |
start = str.IndexOf(":", this.pos) + 1; | |
end = str.IndexOf(":", start); | |
stLen = str.Substring(start, end - start); | |
int bytelen = Int32.Parse(stLen); | |
length = bytelen; | |
//This is the byte length, not the character length - so we migth | |
//need to shorten it before usage. This also implies bounds checking | |
if ((end + 2 + length) >= str.Length) length = str.Length - 2 - end; | |
string stRet = str.Substring(end + 2, length); | |
while (this.StringEncoding.GetByteCount(stRet) > bytelen) | |
{ | |
length--; | |
stRet = str.Substring(end + 2, length); | |
} | |
this.pos += 6 + stLen.Length + length; | |
if (this.XMLSafe) | |
{ | |
stRet = stRet.Replace("\n", "\r\n"); | |
} | |
return stRet; | |
case 'a': | |
//if keys are ints 0 through N, returns an ArrayList, else returns Hashtable | |
start = str.IndexOf(":", this.pos) + 1; | |
end = str.IndexOf(":", start); | |
stLen = str.Substring(start, end - start); | |
length = Int32.Parse(stLen); | |
Hashtable htRet = new Hashtable(length); | |
ArrayList alRet = new ArrayList(length); | |
this.pos += 4 + stLen.Length; //a:Len:{ | |
for (int i = 0; i < length; i++) | |
{ | |
//read key | |
object key = deserialize(str); | |
//read value | |
object val = deserialize(str); | |
if (alRet != null) | |
{ | |
if (key is int && (int)key == alRet.Count) | |
alRet.Add(val); | |
else | |
alRet = null; | |
} | |
htRet[key] = val; | |
} | |
this.pos++; //skip the } | |
if (this.pos < str.Length && str[this.pos] == ';')//skipping our old extra array semi-colon bug (er... php's weirdness) | |
this.pos++; | |
if (alRet != null) | |
return alRet; | |
else | |
return htRet; | |
default: | |
return ""; | |
}//switch | |
}//unserialzie(object) | |
}//class Serializer |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Changed all .indexOf from original gist to avoid deadly performance issue on .Net Core 3.1 + Linux .