diff --git a/ToonFormat.slnx b/ToonFormat.slnx
new file mode 100644
index 0000000..4dd3dcb
--- /dev/null
+++ b/ToonFormat.slnx
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/ToonFormat/Constants.cs b/src/ToonFormat/Constants.cs
index a16ec88..2505013 100644
--- a/src/ToonFormat/Constants.cs
+++ b/src/ToonFormat/Constants.cs
@@ -78,7 +78,7 @@ public static bool IsStructural(char c)
/// TOON's unified options configuration, styled to align with System.Text.Json. Used to control indentation,
/// delimiters, strict mode, length markers, and underlying JSON behavior.
///
- public enum ToonDelimiter
+ public enum ToonDelimiter : byte
{
/// Comma ,
COMMA,
diff --git a/src/ToonFormat/Internal/Decode/Scanner.cs b/src/ToonFormat/Internal/Decode/Scanner.cs
index b14c27d..b328d3e 100644
--- a/src/ToonFormat/Internal/Decode/Scanner.cs
+++ b/src/ToonFormat/Internal/Decode/Scanner.cs
@@ -1,7 +1,6 @@
#nullable enable
using System;
using System.Collections.Generic;
-using System.Linq;
namespace ToonFormat.Internal.Decode
{
@@ -10,7 +9,6 @@ namespace ToonFormat.Internal.Decode
///
internal class ParsedLine
{
- public string Raw { get; set; } = string.Empty;
public int Indent { get; set; }
public string Content { get; set; } = string.Empty;
public int Depth { get; set; }
@@ -188,7 +186,6 @@ public static ScanResult ToParsedLines(string source, int indentSize, bool stric
}
parsed.Add(new ParsedLine
{
- Raw = new string(lineSpan),
Indent = indent,
Content = new string(contentSpan),
Depth = lineDepth,
diff --git a/src/ToonFormat/Internal/Encode/LineWriter.cs b/src/ToonFormat/Internal/Encode/LineWriter.cs
index fb3409f..1fe76cb 100644
--- a/src/ToonFormat/Internal/Encode/LineWriter.cs
+++ b/src/ToonFormat/Internal/Encode/LineWriter.cs
@@ -10,8 +10,10 @@ namespace ToonFormat.Internal.Encode
///
internal class LineWriter
{
- private readonly List _lines = new();
- private readonly string _indentationString;
+ private readonly StringBuilder _builder = new();
+ private readonly string _indentationUnit;
+ private readonly List _indentCache = new() { string.Empty };
+ private bool _hasAnyLine;
///
/// Creates a new LineWriter with the specified indentation size.
@@ -19,7 +21,7 @@ internal class LineWriter
/// Number of spaces per indentation level.
public LineWriter(int indentSize)
{
- _indentationString = new string(' ', indentSize);
+ _indentationUnit = new string(' ', indentSize);
}
///
@@ -29,8 +31,17 @@ public LineWriter(int indentSize)
/// The content of the line.
public void Push(int depth, string content)
{
- var indent = RepeatString(_indentationString, depth);
- _lines.Add(indent + content);
+ if (_hasAnyLine)
+ {
+ _builder.Append('\n');
+ }
+ else
+ {
+ _hasAnyLine = true;
+ }
+
+ _builder.Append(GetIndent(depth));
+ _builder.Append(content);
}
///
@@ -48,23 +59,20 @@ public void PushListItem(int depth, string content)
///
public override string ToString()
{
- return string.Join("\n", _lines);
+ return _builder.ToString();
}
- ///
- /// Helper method to repeat a string n times.
- ///
- private static string RepeatString(string str, int count)
+ private string GetIndent(int depth)
{
- if (count <= 0)
+ if (depth <= 0)
return string.Empty;
- var sb = new StringBuilder(str.Length * count);
- for (int i = 0; i < count; i++)
+ while (_indentCache.Count <= depth)
{
- sb.Append(str);
+ _indentCache.Add(string.Concat(_indentCache[^1], _indentationUnit));
}
- return sb.ToString();
+
+ return _indentCache[depth];
}
}
}
diff --git a/src/ToonFormat/Internal/Encode/Normalize.cs b/src/ToonFormat/Internal/Encode/Normalize.cs
index a9f2b2c..1ac32c3 100644
--- a/src/ToonFormat/Internal/Encode/Normalize.cs
+++ b/src/ToonFormat/Internal/Encode/Normalize.cs
@@ -102,8 +102,10 @@ internal static class Normalize
var type = value.GetType();
var properties = type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
- foreach (var prop in properties.Where(prop => prop.CanRead))
+ foreach (var prop in properties)
{
+ if (!prop.CanRead)
+ continue;
var propValue = prop.GetValue(value);
jsonObject[prop.Name] = NormalizeValue(propValue);
}
@@ -211,7 +213,7 @@ internal static class Normalize
///
/// Determines if a value is a plain object (not a primitive, collection, or special type).
///
- private static bool IsPlainObject(object value)
+ private static bool IsPlainObject(object? value)
{
if (value == null)
return false;
diff --git a/src/ToonFormat/Internal/Encode/Primitives.cs b/src/ToonFormat/Internal/Encode/Primitives.cs
index ccb9016..ad688b2 100644
--- a/src/ToonFormat/Internal/Encode/Primitives.cs
+++ b/src/ToonFormat/Internal/Encode/Primitives.cs
@@ -94,8 +94,17 @@ public static string EncodeKey(string key)
///
public static string EncodeAndJoinPrimitives(IEnumerable values, char delimiter = Constants.COMMA)
{
- var encoded = values.Select(v => EncodePrimitive(v, delimiter));
- return string.Join(delimiter.ToString(), encoded);
+ var sb = new StringBuilder();
+ bool first = true;
+ foreach (var value in values)
+ {
+ if (!first)
+ sb.Append(delimiter);
+ first = false;
+
+ sb.Append(EncodePrimitive(value, delimiter));
+ }
+ return sb.ToString();
}
// #endregion
@@ -116,32 +125,36 @@ public static string FormatHeader(
char? delimiter = null)
{
var delimiterChar = delimiter ?? Constants.DEFAULT_DELIMITER_CHAR;
- var header = string.Empty;
+ var sb = new StringBuilder();
// Add key if present
if (!string.IsNullOrEmpty(key))
- {
- header += EncodeKey(key);
- }
+ sb.Append(EncodeKey(key));
// Add array length with optional marker and delimiter
- var delimiterSuffix = delimiterChar != Constants.DEFAULT_DELIMITER_CHAR
- ? delimiterChar.ToString()
- : string.Empty;
-
- header += $"{Constants.OPEN_BRACKET}{length}{delimiterSuffix}{Constants.CLOSE_BRACKET}";
+ sb.Append(Constants.OPEN_BRACKET);
+ if (lengthMarker)
+ sb.Append(Constants.HASH);
+ sb.Append(length);
+ if (delimiterChar != Constants.DEFAULT_DELIMITER_CHAR)
+ sb.Append(delimiterChar);
+ sb.Append(Constants.CLOSE_BRACKET);
// Add field names for tabular format
- if (fields != null && fields.Count > 0)
+ if (fields is { Count: > 0 })
{
- var quotedFields = fields.Select(EncodeKey);
- var fieldsStr = string.Join(delimiterChar.ToString(), quotedFields);
- header += $"{Constants.OPEN_BRACE}{fieldsStr}{Constants.CLOSE_BRACE}";
+ sb.Append(Constants.OPEN_BRACE);
+ for (int i = 0; i < fields.Count; i++)
+ {
+ if (i > 0)
+ sb.Append(delimiterChar);
+ sb.Append(EncodeKey(fields[i]));
+ }
+ sb.Append(Constants.CLOSE_BRACE);
}
- header += Constants.COLON;
-
- return header;
+ sb.Append(Constants.COLON);
+ return sb.ToString();
}
// #endregion
diff --git a/src/ToonFormat/ToonDecoder.cs b/src/ToonFormat/ToonDecoder.cs
index dc1551f..e393279 100644
--- a/src/ToonFormat/ToonDecoder.cs
+++ b/src/ToonFormat/ToonDecoder.cs
@@ -99,6 +99,54 @@ public static class ToonDecoder
return JsonSerializer.Deserialize(json);
}
+ ///
+ /// Decodes TOON data from a UTF-8 byte span into a JsonNode with default options.
+ ///
+ /// UTF-8 encoded TOON text.
+ public static JsonNode? Decode(ReadOnlySpan utf8Bytes)
+ {
+ return Decode(utf8Bytes, new ToonDecodeOptions());
+ }
+
+ ///
+ /// Decodes TOON data from a UTF-8 byte span into a JsonNode with custom options.
+ ///
+ /// UTF-8 encoded TOON text.
+ /// Decoding options to customize parsing behavior.
+ public static JsonNode? Decode(ReadOnlySpan utf8Bytes, ToonDecodeOptions? options)
+ {
+ if (options == null)
+ throw new ArgumentNullException(nameof(options));
+
+ var text = Encoding.UTF8.GetString(utf8Bytes);
+ return Decode(text, options);
+ }
+
+ ///
+ /// Decodes TOON data from a UTF-8 byte span into the specified type with default options.
+ ///
+ /// Target type to deserialize into.
+ /// UTF-8 encoded TOON text.
+ public static T? Decode(ReadOnlySpan utf8Bytes)
+ {
+ return Decode(utf8Bytes, new ToonDecodeOptions());
+ }
+
+ ///
+ /// Decodes TOON data from a UTF-8 byte span into the specified type with custom options.
+ ///
+ /// Target type to deserialize into.
+ /// UTF-8 encoded TOON text.
+ /// Decoding options to customize parsing behavior.
+ public static T? Decode(ReadOnlySpan utf8Bytes, ToonDecodeOptions? options)
+ {
+ if (options == null)
+ throw new ArgumentNullException(nameof(options));
+
+ var text = Encoding.UTF8.GetString(utf8Bytes);
+ return Decode(text, options);
+ }
+
///
/// Decodes TOON data from a UTF-8 byte array into a JsonNode with default options.
///
diff --git a/src/ToonFormat/ToonSerializer.cs b/src/ToonFormat/ToonSerializer.cs
new file mode 100644
index 0000000..ddeb552
--- /dev/null
+++ b/src/ToonFormat/ToonSerializer.cs
@@ -0,0 +1,199 @@
+using System;
+using System.IO;
+using System.Text.Json.Nodes;
+using Toon.Format;
+
+namespace ToonFormat;
+
+///
+/// Provides a concise API similar to System.Text.Json.JsonSerializer for converting between TOON format and objects/JSON.
+///
+public static class ToonSerializer
+{
+ #region Serialize (Object to TOON)
+
+ ///
+ /// Serializes the specified value to a TOON-formatted string.
+ ///
+ /// The type of the value to serialize.
+ /// The value to serialize.
+ /// A TOON-formatted string representation of the value.
+ public static string Serialize(T value)
+ {
+ return ToonEncoder.Encode(value);
+ }
+
+ ///
+ /// Serializes the specified value to a TOON-formatted string with custom options.
+ ///
+ /// The type of the value to serialize.
+ /// The value to serialize.
+ /// Options to control serialization behavior.
+ /// A TOON-formatted string representation of the value.
+ public static string Serialize(T value, ToonEncodeOptions? options)
+ {
+ return ToonEncoder.Encode(value, options ?? new ToonEncodeOptions());
+ }
+
+ ///
+ /// Serializes the specified value to a UTF-8 encoded TOON byte array.
+ ///
+ /// The type of the value to serialize.
+ /// The value to serialize.
+ /// A UTF-8 encoded byte array containing the TOON representation.
+ public static byte[] SerializeToUtf8Bytes(T value)
+ {
+ return ToonEncoder.EncodeToBytes(value);
+ }
+
+ ///
+ /// Serializes the specified value to a UTF-8 encoded TOON byte array with custom options.
+ ///
+ /// The type of the value to serialize.
+ /// The value to serialize.
+ /// Options to control serialization behavior.
+ /// A UTF-8 encoded byte array containing the TOON representation.
+ public static byte[] SerializeToUtf8Bytes(T value, ToonEncodeOptions? options)
+ {
+ return ToonEncoder.EncodeToBytes(value, options ?? new ToonEncodeOptions());
+ }
+
+ ///
+ /// Serializes the specified value and writes it to the specified stream.
+ ///
+ /// The type of the value to serialize.
+ /// The stream to write the TOON representation to.
+ /// The value to serialize.
+ public static void Serialize(Stream stream, T value)
+ {
+ ToonEncoder.EncodeToStream(value, stream);
+ }
+
+ ///
+ /// Serializes the specified value and writes it to the specified stream with custom options.
+ ///
+ /// The type of the value to serialize.
+ /// The stream to write the TOON representation to.
+ /// The value to serialize.
+ /// Options to control serialization behavior.
+ public static void Serialize(Stream stream, T value, ToonEncodeOptions? options)
+ {
+ ToonEncoder.EncodeToStream(value, stream, options ?? new ToonEncodeOptions());
+ }
+
+ #endregion
+
+ #region Deserialize (TOON to Object)
+
+ ///
+ /// Deserializes the TOON-formatted string to the specified type.
+ ///
+ /// The type to deserialize to.
+ /// The TOON-formatted string to deserialize.
+ /// The deserialized value of type T.
+ /// Thrown when toon is null.
+ /// Thrown when the TOON format is invalid.
+ public static T? Deserialize(string toon)
+ {
+ return ToonDecoder.Decode(toon);
+ }
+
+ ///
+ /// Deserializes the TOON-formatted string to the specified type with custom options.
+ ///
+ /// The type to deserialize to.
+ /// The TOON-formatted string to deserialize.
+ /// Options to control deserialization behavior.
+ /// The deserialized value of type T.
+ /// Thrown when toon or options is null.
+ /// Thrown when the TOON format is invalid.
+ public static T? Deserialize(string toon, ToonDecodeOptions? options)
+ {
+ return ToonDecoder.Decode(toon, options ?? new ToonDecodeOptions());
+ }
+
+ ///
+ /// Deserializes the UTF-8 encoded TOON byte array to the specified type.
+ ///
+ /// The type to deserialize to.
+ /// The UTF-8 encoded TOON byte array to deserialize.
+ /// The deserialized value of type T.
+ /// Thrown when utf8Toon is null.
+ /// Thrown when the TOON format is invalid.
+ public static T? Deserialize(ReadOnlySpan utf8Toon)
+ {
+ return ToonDecoder.Decode(utf8Toon);
+ }
+
+ ///
+ /// Deserializes the UTF-8 encoded TOON byte array to the specified type with custom options.
+ ///
+ /// The type to deserialize to.
+ /// The UTF-8 encoded TOON byte array to deserialize.
+ /// Options to control deserialization behavior.
+ /// The deserialized value of type T.
+ /// Thrown when options is null.
+ /// Thrown when the TOON format is invalid.
+ public static T? Deserialize(ReadOnlySpan utf8Toon, ToonDecodeOptions? options)
+ {
+ return ToonDecoder.Decode(utf8Toon, options ?? new ToonDecodeOptions());
+ }
+
+ ///
+ /// Deserializes the TOON data from the specified stream to the specified type.
+ ///
+ /// The type to deserialize to.
+ /// The stream containing TOON data to deserialize.
+ /// The deserialized value of type T.
+ /// Thrown when stream is null.
+ /// Thrown when the TOON format is invalid.
+ public static T? Deserialize(Stream stream)
+ {
+ return ToonDecoder.Decode(stream);
+ }
+
+ ///
+ /// Deserializes the TOON data from the specified stream to the specified type with custom options.
+ ///
+ /// The type to deserialize to.
+ /// The stream containing TOON data to deserialize.
+ /// Options to control deserialization behavior.
+ /// The deserialized value of type T.
+ /// Thrown when stream or options is null.
+ /// Thrown when the TOON format is invalid.
+ public static T? Deserialize(Stream stream, ToonDecodeOptions? options)
+ {
+ return ToonDecoder.Decode(stream, options ?? new ToonDecodeOptions());
+ }
+
+ #endregion
+
+ #region JsonNode Conversion
+
+ ///
+ /// Deserializes the TOON-formatted string to a JsonNode.
+ ///
+ /// The TOON-formatted string to deserialize.
+ /// The deserialized JsonNode.
+ /// Thrown when toon is null.
+ /// Thrown when the TOON format is invalid.
+ public static JsonNode? DeserializeToJsonNode(string toon)
+ {
+ return ToonDecoder.Decode(toon);
+ }
+
+ ///
+ /// Deserializes the TOON-formatted string to a JsonNode with custom options.
+ ///
+ /// The TOON-formatted string to deserialize.
+ /// Options to control deserialization behavior.
+ /// The deserialized JsonNode.
+ /// Thrown when toon or options is null.
+ /// Thrown when the TOON format is invalid.
+ public static JsonNode? DeserializeToJsonNode(string toon, ToonDecodeOptions? options)
+ {
+ return ToonDecoder.Decode(toon, options ?? new ToonDecodeOptions());
+ }
+
+ #endregion
+}
\ No newline at end of file