/// <summary> /// Turns a whole object into ABSave, excluding everything extra, and places the result in a StringBuilder. /// </summary> public static string ObjectToABSave(object obj, ABSaveType type, ABSaveSettings settings) { var sb = new StringBuilder(); ObjectToABSave(obj, type, sb, settings); return(sb.ToString()); }
public static void WriteHeader(ABSaveType type, StringBuilder sb, ABSaveSettings settings, bool writeHeader) { // If we shouldn't write the header, then don't do it. if (!writeHeader) { return; } // Insert the type ("U" for unnamed and "N" for named and types, "V" for unnamed without types, "M" for named without types) if (settings.WithTypes) { sb.Append(type == ABSaveType.NoNames ? 'U' : 'N'); } else { sb.Append(type == ABSaveType.NoNames ? 'V' : 'M'); } // Insert version number - if needed. if (settings.HasVersion) { sb.Append(settings.Version); } // End the header off. sb.Append('\u0001'); }
internal static void SerializeArrayItem(ABSaveType type, ABSaveSettings settings, bool useSB, StringBuilder sb, bool dnWriteEndLevel, ref string ret, ref bool notFirst, ref ABSavePrimitiveType lastType, dynamic item) { // Serialize the item and write to either the StringBuilder or the "ret"... ret += Serialize(item, type, settings, out lastType, useSB, sb, RequiresLowerInnerLevelSymbol(lastType) ? false : notFirst, dnWriteEndLevel); // Update the "notFirst" variable. if (!notFirst) { notFirst = true; } }
/// <summary> /// Deserializes an ABSave document into a whole object. /// </summary> /// <typeparam name="T">The type of the object to deserialize into.</typeparam> /// <param name="str">The string to deserialize from.</param> /// <param name="type">The type of the ABSave Document.</param> /// <param name="errorHandler">The way of handling errors through the process.</param> /// <returns></returns> public static T ABSaveToObject <T>(string str, ABSaveType type, ABSaveSettings errorHandler = null) { // Create a new parser. var parser = new ABSaveParser <T>(type, errorHandler); // Start the new parser. parser.Start(str); // Return the result. return(parser.Result); }
// An internal helper that automatically decides which version of "ObjectToABSave" to use. internal static string ObjectToABSave(object obj, ABSaveType type, ABSaveSettings settings, bool writeToSB = false, StringBuilder sb = null) { if (writeToSB) { ObjectToABSave(obj, type, sb, settings); } else { return(ObjectToABSave(obj, type, settings)); } return(""); }
/// <summary> /// Turns a whole object into an ABSave document, and places the result in a StringBuilder. /// </summary> /// <param name="obj">The object to convert.</param> /// <param name="type">The way of serializing this ABSave (unnamed/named).</param> /// <param name="sb">The StringBuilder to write to</param> /// <param name="settings">The settings for how to handle certain parts.</param> /// <param name="writeHeader">Whether we should write the header in or not.</param> public static void ObjectToABSaveDocument(object obj, ABSaveType type, StringBuilder sb, ABSaveSettings settings, bool writeHeader = true) { // If the type is invalid, throw an exception. if (type == ABSaveType.Infer) { settings.ErrorHandler.InferTypeWhenSerializing(); return; } // Write the header. ABSaveWriter.WriteHeader(type, sb, settings, writeHeader); // Write the actual object. ObjectToABSave(obj, type, sb, settings); }
/// <summary> /// The main parser which is used over and over again to parse strings. /// </summary> //internal static ABSaveParser<T> MainParser; /// <summary> /// Turns a whole object into a string with an ABSave document. /// </summary> /// <param name="obj">The object to convert.</param> /// <param name="type">The way of serializing this ABSave (unnamed/named).</param> /// <param name="settings">The settings for how to handle certain parts.</param> /// <param name="writeHeader">Whether we should write the header in or not.</param> /// <returns>The ABSave string.</returns> public static string ObjectToABSaveDocument(object obj, ABSaveType type, ABSaveSettings settings, bool writeHeader = true) { var ret = new StringBuilder(); // NOTE: (tl;dr) // Why do we use a StringBuilder, and not just a string? // Well, there's one word to explain that: // Performance. // The ONLY reason we are using a StringBuilder instead of a string is because it's much faster. // HOWEVER, we don't want to just support StringBuilder - we want to allow users to use strings as well. // And THAT'S why we have a string mode (just set UseSB to false on most methods) and a StringBuilder mode! // StringBuilder is faster, but less flexible - I mean, you can do basically anything with a string, because it's a standard type. ObjectToABSaveDocument(obj, type, ret, settings, writeHeader); return(ret.ToString()); }
/// <summary> /// Serializes a dictionary. /// </summary> /// <param name="obj">The dictionary to serialize.</param> /// <param name="useSB">Whether this will write to a string builder (if true), or return a string (if false).</param> /// <param name="sb">The StringBuilder to write to - if <paramref name="useSB"/> is true.</param> /// <param name="dnWriteEndLevel">"Do Not Write End Level Symbol" - Marks whether to NOT write \u0005 (if true), commonly used for the last object of all.</param> /// <returns>If <paramref name="useSB"/> is false, this method will return the result as a string.</returns> public static string SerializeDictionary(dynamic obj, ABSaveType type, ABSaveSettings settings, bool useSB = false, StringBuilder sb = null, bool dnWriteEndLevel = false) { // Create a variable to store what we'll return. var ret = ""; // Keep track of whether we're on the first item or not. var notFirst = false; // Write the opening for the array. ret += ABSaveWriter.WriteDictionaryOpening(useSB, sb); // Now, go through each item in the dictionary. foreach (var element in obj) { // If this isn't the first item, write the "next item" character. if (notFirst) { ret += ABSaveWriter.WriteNextItem(true, useSB, sb); } // Add the key to it. if (useSB) { sb.Append(element.Key); } else { ret += element.Key; } // Serialize the item and write to either the StringBuilder or the "ret" ret += Serialize(element.Value, type, settings, useSB, sb, true, dnWriteEndLevel); // Update the "notFirst" variable if needed. if (!notFirst) { notFirst = true; } } // Write the closing for the dictionary. ret += ABSaveWriter.WriteObjectClose(dnWriteEndLevel, useSB, sb); // Now, "ret" would be empty if we were using a StringBuilder, however, if we weren't... It will have the correct string in it so return it. return(ret); }
/// <summary> /// Gets the ABSave info from the header - called from <see cref="EstablishedHeader"/>. /// </summary> /// <param name="leading">The lowercase leading.</param> private void GetTypeFromHeader(string leading) { // Get the first character - now that we've confirmed there's at least ONE character in the leading. var firstChar = leading[0]; switch (firstChar) { case 'u': // Unnamed ABSaveType = ABSaveType.NoNames; // Has Types HasTypes = true; break; case 'n': // Named ABSaveType = ABSaveType.WithNames; // Has Types HasTypes = true; break; case 'v': // Unnamed ABSaveType = ABSaveType.NoNames; // No Types HasTypes = false; break; case 'm': // Named ABSaveType = ABSaveType.WithNames; // No Types HasTypes = false; break; default: // If the first character isn't any of them, it's invalid. Settings.ErrorHandler.InvalidHeaderWhenParsing("The first character of the header provided is not valid ('U' or 'N')."); return; } }
/// <summary> /// Creates a new ABSaveParser with all the correct configuration. /// </summary> /// <param name="objType">The type of the object to parse.</param> /// <param name="type">The way to handle the ABSave string.</param> /// <param name="settings">The settings for it.</param> public ABSaveParser(ABSaveType type, ABSaveSettings settings) { // Set all the tokens for the ABSave. Tokens = new System.Collections.ObjectModel.ObservableCollection <ABParserToken>() { new ABParserToken(nameof(ABSaveTokens.NextItem), '\u0001'), new ABParserToken(nameof(ABSaveTokens.Null), '\u0002'), new ABParserToken(nameof(ABSaveTokens.StartObject), '\u0003'), new ABParserToken(nameof(ABSaveTokens.StartArray), '\u0004'), new ABParserToken(nameof(ABSaveTokens.ExitObject), '\u0005'), new ABParserToken(nameof(ABSaveTokens.StartDictionary), '\u0006'), }; // Set the correct settings. Settings = settings; Settings.ErrorHandler.ErrorEncountered += (e) => IsProcessing = false; // Set the type for now. ABSaveType = type; }
/// <summary> /// An object (with multiple properties) to serialize manually - one that doesn't have a TypeConverter. /// </summary> /// <param name="obj">The object to serialize manually</param> /// <param name="objType">The type of the object.</param> /// <param name="useSB">Whether this will write to a string builder (if true), or return a string (if false).</param> /// <param name="sb">The StringBuilder to write to - if <paramref name="useSB"/> is true.</param> /// <param name="dnWriteEndLevel">"Do Not Write End Level Symbol" - Marks whether to NOT write \u0005 (if true), commonly used for the last object of all.</param> /// <returns>If <paramref name="useSB"/> is false, this method will return the result as a string.</returns> public static string SerializeArray(dynamic obj, Type objType, ABSaveType type, ABSaveSettings settings, bool useSB = false, StringBuilder sb = null, bool dnWriteEndLevel = false) { obj = obj as IEnumerable; // Create a variable to store what we'll return. var ret = ""; // Keep track of whether we're on the first item or not. var notFirst = false; // Write the opening for the array. ret += ABSaveWriter.WriteArrayOpening(useSB, sb); // Keep track of what type the last property was - this allows us to decide whether to add the Next Item character to the next item. var lastType = ABSavePrimitiveType.Unknown; // If it's an array, just use a "for" loop. if (objType.IsArray) { for (var i = 0; i < obj.Length; i++) { SerializeArrayItem(type, settings, useSB, sb, dnWriteEndLevel, ref ret, ref notFirst, ref lastType, obj[i]); } } // For anything else, foreach will work fine. else { foreach (var item in obj) { SerializeArrayItem(type, settings, useSB, sb, dnWriteEndLevel, ref ret, ref notFirst, ref lastType, item); } } // Write the closing for the array. ret += ABSaveWriter.WriteObjectClose(dnWriteEndLevel, useSB, sb); // Now, "ret" would be empty if we were using a StringBuilder, however, if we weren't... It will have the correct string in it so return it. return(ret); }
static void ConvertVariableToABSave(ABSaveType type, StringBuilder sb, ABSaveSettings settings, Helpers.ABSaveObjectItems members, ref bool notFirst, ref ABSavePrimitiveType lastType, int i) { // If we're doing it named - write the name. if (type == ABSaveType.WithNames) { // Write the name out, don't write the Next Instruction character if it's the first item or the last item had a "lowerInnerlevel" sign after it. ABSaveWriter.WriteString(members.Items[i].Info.Name, ABSaveUtils.RequiresLowerInnerLevelSymbol(lastType) ? false : notFirst, true, sb); // Since we've written the name out... And the "notFirst" variable is used to determine whether to write the next instruction symbol or not... Set "notFirst" to true since it will HAVE to have the next instruction symbol now. notFirst = true; } // Serialize each variable, to the StringBuilder. If the last member was an array or object, then instead of getting it to write the // "next instruction" character, we need to get it to write the "lower" symbol instead. ABSaveSerializer.Serialize(members.Items[i].Value, type, settings, out lastType, true, sb, ABSaveUtils.RequiresLowerInnerLevelSymbol(lastType) ? false : notFirst, i == members.Count - 1); // Update the "notFirst" variable if it's false and we've gone through one item. if (!notFirst) { notFirst = true; } }
/// <summary> /// Serializes an object, by using <see cref="ABSaveConvert"/>, but some extras. /// </summary> /// <param name="obj">The object to serialize manually.</param> /// <param name="objType">The type of the object to serialize manually.</param> /// <param name="useSB">Whether this will write to a string builder (if true), or return a string (if false).</param> /// <param name="sb">The StringBuilder to write to - if <paramref name="useSB"/> is true.</param> /// <param name="dnWriteEndLevel">"Do Not Write End Level Symbol" - Marks whether to NOT write \u0005 (if true), commonly used for the last object of all.</param> /// <param name="settings">The settings for how to handle certain parts.</param> /// <returns>If <paramref name="useSB"/> is false, this method will return the result as a string.</returns> public static string SerializeObject(object obj, ABSaveType type, Type objType, ABSaveSettings settings, bool writeNextInstructionSymbol = true, bool useSB = false, StringBuilder sb = null, bool dnWriteEndLevel = false) { // Create a variable to store what we'll return - if we aren't using a StringBuilder. var ret = ""; // First, write a "next step" symbol. ret += ABSaveWriter.WriteNextItem(writeNextInstructionSymbol, useSB, sb); // Next, serialize the type that goes before it. ret += SerializeTypeBeforeObject(objType, settings, useSB, sb); // Then, write the opening (\u0003) for the object. ret += ABSaveWriter.WriteObjectOpen(useSB, sb); // And, write the actual object, use the correct method for either string or for a StringBuilder. ret += ABSaveConvert.ObjectToABSave(obj, type, settings, useSB, sb); // Finally, write the ending for the object. ret += ABSaveWriter.WriteObjectClose(dnWriteEndLevel, useSB, sb); // Now, "ret" would be empty if we were using a StringBuilder, however, if we weren't... It will have the correct string in it so return it. return(ret); }
/// <summary> /// Turns a whole object into ABSave, and places the result in a StringBuilder. /// (Does not include anything extra, such as "\u0003" or the type that goes before - for that, use <see cref="ABSaveSerializer.SerializeObject(object, ABSaveType, System.Type, ABSaveSettings, bool, bool, StringBuilder, bool)"/>) /// </summary> public static void ObjectToABSave(object obj, ABSaveType type, StringBuilder sb, ABSaveSettings settings) { // If the object is null, don't bother with doing anything. if (obj == null) { return; } // Get all of the variables inside this object. var members = ABSaveUtils.GetFieldsAndPropertiesWithValues(obj); // This is a variable used across the whole process to decide whether this is the first one or not (to write the "Next Item" character or not). var notFirst = false; // Keep track of what type the last property was - this allows us to decide whether to add the Next Item character to the next item. var lastType = ABSavePrimitiveType.Unknown; // Go through each variable, and process it. for (var i = 0; i < members.Count; i++) { ConvertVariableToABSave(type, sb, settings, members, ref notFirst, ref lastType, i); } }
/// <summary> /// Serializes an object. The object could be anything. /// </summary> /// <param name="obj">The object to serialize</param> /// <param name="useSB">Whether this will write to a string builder (if true), or return a string (if false).</param> /// <param name="sb">The StringBuilder to write to - if <paramref name="useSB"/> is true.</param> /// <param name="determinedType">The primitive type which has been decided for it.</param> /// <param name="writeNextInstructionSymbol">Whether it will write \u0001 on the start - usually false if it is serializing the first object in a class.</param> /// <param name="dnWriteEndLevel">"Do Not Write End Level Symbol" - Marks whether to NOT write \u0005 (if true), commonly used for the last object of all.</param> /// <returns>If <paramref name="useSB"/> is false, this method will return the result as a string.</returns> public static string Serialize(dynamic obj, ABSaveType type, ABSaveSettings settings, out ABSavePrimitiveType determinedType, bool useSB = false, StringBuilder sb = null, bool writeNextInstructionSymbol = true, bool dnWriteEndLevel = false) { // This will be what to return if useSB is false. string ret; // For now, make it so we output "unknown". determinedType = ABSavePrimitiveType.Unknown; // Check if the object is null... or an IntPtr, write the null symbol - otherwise, we could get a StackOverflowException. if (obj == null || obj is IntPtr || obj is UIntPtr) { return(ABSaveWriter.WriteNullItem(useSB, sb)); } // Remember what type the object is - as well as the TypeCode. Type objType = obj.GetType(); var tCode = Type.GetTypeCode(obj.GetType()); // If the object is a string - write it as a string. if (tCode == TypeCode.String) { ret = ABSaveWriter.WriteString(obj, writeNextInstructionSymbol, useSB, sb); determinedType = ABSavePrimitiveType.String; } // If the object is a number - write it as a number. else if (IsNumericType(tCode)) { ret = ABSaveWriter.WriteNumerical(obj, tCode, true, useSB, sb); determinedType = ABSavePrimitiveType.Number; } // If the object is an array - serialize it as an array. else if (IsArray(objType)) { ret = SerializeArray(obj, objType, type, settings, useSB, sb, dnWriteEndLevel); determinedType = ABSavePrimitiveType.Array; } // If the object is a dictionary - serialize it as a dictionary. else if (IsDictionary(objType)) { ret = SerializeDictionary(obj, type, settings, useSB, sb, dnWriteEndLevel); determinedType = ABSavePrimitiveType.Dictionary; } // If the object is a boolean - serialize it as a boolean. else if (tCode == TypeCode.Boolean) { ret = SerializeBool(obj, writeNextInstructionSymbol, useSB, sb); determinedType = ABSavePrimitiveType.Boolean; } // If the object is a DateTime - serialize it as a DateTime. else if (tCode == TypeCode.DateTime) { ret = SerializeDateTime(obj, useSB, sb); determinedType = ABSavePrimitiveType.DateTime; } // If it's a type, just write it out using the ABSaveWriter (for some reason there is no TypeConverter built-in for a type!) else if (obj is Type) { ret = SerializeType(obj, useSB, sb); determinedType = ABSavePrimitiveType.Type; } // Otherwise, we'll attempt to find a built-in type converter (to a string) else { // Mark it as an object. determinedType = ABSavePrimitiveType.Object; // Attempt to get a converter for it. var canBeTypeConverted = false; var typeConv = TypeDescriptor.GetConverter(objType); // Check if the type converter can actually convert it to a string. if (typeConv.IsValid(obj)) { if (typeConv.CanConvertTo(typeof(string))) { canBeTypeConverted = true; } } // If it can be type converted, convert it using that, and then write it as a string. if (canBeTypeConverted) { ret = ABSaveWriter.WriteString(typeConv.ConvertToString(obj), writeNextInstructionSymbol, useSB, sb); } // Otherwise, if it can't be type converted... Manually convert it. else { ret = SerializeObject(obj, type, objType, settings, writeNextInstructionSymbol, useSB, sb, dnWriteEndLevel); } } // Return the result from this. return(ret); }
/// <summary> /// Serializes an object. The object could be anything. /// </summary> /// <param name="obj">The object to serialize</param> /// <param name="useSB">Whether this will write to a string builder (if true), or return a string (if false).</param> /// <param name="sb">The StringBuilder to write to - if <paramref name="useSB"/> is true.</param> /// <param name="writeNextInstructionSymbol">Whether it will write \u0001 on the start - usually false if it is serializing the first object in a class.</param> /// <param name="dnWriteEndLevel">"Do Not Write End Level Symbol" - Marks whether to NOT write \u0005 (if true), commonly used for the last object of all.</param> /// <param name="settings">The settings for how to handle certain parts.</param> /// <returns>If <paramref name="useSB"/> is false, this method will return the result as a string.</returns> public static string Serialize(dynamic obj, ABSaveType type, ABSaveSettings settings, bool useSB = false, StringBuilder sb = null, bool writeNextInstructionSymbol = true, bool dnWriteEndLevel = false) { return(Serialize(obj, type, settings, out ABSavePrimitiveType _, useSB, sb, writeNextInstructionSymbol, dnWriteEndLevel)); }