// JValue - JString using System; using System.Text; using Leguar.TotalJSON.Internal; namespace Leguar.TotalJSON { /// /// Class to store string value in JSON format. Once JString instance is created, its value can't be changed. /// public class JString : JValue { // , IEquatable { private const string HEX="0123456789ABCDEF"; private readonly string stringValue; /// /// Creates new instance of JString object. /// /// Parameter can't be null. If you wish to add null to JSON or JArray object, create new JNull() and add that one. /// /// /// C# string value to be stored in this object. /// /// /// If parameter is null. /// public JString(string stringValue) : base() { if (stringValue==null) { throw (new JArgumentNullException("stringValue","Parameter can not be null in constructor JString.(string)")); } this.stringValue=stringValue; } /// /// Returns compact information of this JString object as string, for debug purposes. /// /// /// Single string with information of this JString object. /// public override string ToString() { int length = stringValue.Length; if (length==0) { return ("[JString: Empty string]"); } else { return ("[JString: \""+stringValue+"\" ("+length+" character"+(length>1?"s":"")+")]"); } } /// /// Test if another object equals to this object. Always returns false if parameter object is null or it is not instance of JString. /// Two JString objects are equal if both contains exactly same string. /// /// /// Another object that is compared to this one. /// /// /// True if objects are equal, false otherwise. /// public override bool Equals(object anotherObject) { if (anotherObject==null) { return false; } if (!(anotherObject is JString)) { return false; } JString anotherJString=(JString)(anotherObject); return (stringValue.Equals(anotherJString.AsString())); } public override int GetHashCode() { return stringValue.GetHashCode(); } /// /// Get value of this JSON string as c# system string. /// /// /// c# string value, can not be null. /// public string AsString() { return stringValue; } internal override void zCreate(CreateStringRunner createStringRunner) { encode(createStringRunner,stringValue,true); } internal static void encode(CreateStringRunner createStringRunner, string str, bool isValue) { createStringRunner.append('"'); if (isValue) { createStringRunner.appendColoring(CreateStringRunner.COLOR_STRING_VALUE); } foreach (char chr in str) { if (chr=='"') { createStringRunner.append("\\\""); } else if (chr=='\\') { createStringRunner.append("\\\\"); } else if (chr=='/' && createStringRunner.isEscapeForwardSlashes()) { createStringRunner.append("\\/"); } else if (chr=='\b') { createStringRunner.append("\\b"); } else if (chr=='\f') { createStringRunner.append("\\f"); } else if (chr=='\n') { createStringRunner.append("\\n"); } else if (chr=='\r') { createStringRunner.append("\\r"); } else if (chr=='\t') { createStringRunner.append("\\t"); } else if (chr<32 || chr>126) { if (chr<16) { createStringRunner.append("\\u000"); createStringRunner.append(HEX[chr]); } else if (chr<256) { createStringRunner.append("\\u00"); createStringRunner.append(HEX[chr/16]); createStringRunner.append(HEX[chr%16]); } else if (chr<4096) { createStringRunner.append("\\u0"); createStringRunner.append(HEX[chr/256]); createStringRunner.append(HEX[(chr%256)/16]); createStringRunner.append(HEX[chr%16]); } else { createStringRunner.append("\\u"); createStringRunner.append(HEX[chr/4096]); createStringRunner.append(HEX[(chr%4096)/256]); createStringRunner.append(HEX[(chr%256)/16]); createStringRunner.append(HEX[chr%16]); } } else { if (createStringRunner.isColoredOutput() && (chr == '<' || chr == '>')) { createStringRunner.append("\\u00"); createStringRunner.append(HEX[chr/16]); createStringRunner.append(HEX[chr%16]); } else { createStringRunner.append(chr); } } } if (isValue) { createStringRunner.appendColoring(CreateStringRunner.COLOR_END); } createStringRunner.append('"'); } internal static JString zParse(ParseStringRunner parseStringRunner, bool expectStartOfString) { StringPointer sp = parseStringRunner.getStringPointer(); if (expectStartOfString) { char chr; if (!sp.tryGetNextNonWhiteChar(out chr)) { throw ParseException.forInvalidStart("Parameter string didn't contain any non-white characters",parseStringRunner); } if (chr!='"') { throw ParseException.forInvalidStart("Invalid character "+InternalTools.logSafeCharacter(chr)+" when expecting start of string '\"'", parseStringRunner); } } return (new JString(decode(parseStringRunner))); } internal static string decode(ParseStringRunner parseStringRunner) { // At this point, StringPointer has passed starting " StringPointer sp = parseStringRunner.getStringPointer(); StringBuilder sb=new StringBuilder(); do { char chr=sp.getNextChar(); if (chr=='"') { return sb.ToString(); } if (chr=='\\') { chr=sp.getNextChar(); if (chr=='"' || chr=='\\' || chr=='/') { // 'chr' works as is } else if (chr=='b') { chr='\b'; } else if (chr=='f') { chr='\f'; } else if (chr=='n') { chr='\n'; } else if (chr=='r') { chr='\r'; } else if (chr=='t') { chr='\t'; } else if (chr=='u') { int ucode=0; for (int n=0; n<4; n++) { chr=sp.getNextChar(); int uvalue=HEX.IndexOf(char.ToUpperInvariant(chr)); if (uvalue<0) { throw ParseException.forInvalidCharacter("Invalid hexadecimal character "+InternalTools.logSafeCharacter(chr)+" after '\\u' in string value", parseStringRunner); } ucode=ucode*16+uvalue; } chr=(char)(ucode); } else { throw ParseException.forInvalidCharacter("Invalid character "+InternalTools.logSafeCharacter(chr)+" after '\\' in string value", parseStringRunner); } } sb.Append(chr); } while (true); } internal override object zDeserialize(Type type, string toFieldName, DeserializeSettings deserializeSettings) { if (type==typeof(string)) { return this.AsString(); } if (type==typeof(object)) { if (deserializeSettings.AllowFieldsToBeObjects) { return this.AsString(); } throw (DeserializeException.forNonMatchingTypeObject(this, toFieldName)); } throw (DeserializeException.forNonMatchingType(this,type,toFieldName)); } } }