Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions Ical.Net.Tests/SerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
Expand Down Expand Up @@ -611,4 +612,35 @@ public void TestConvertToInt32WithNegativeNumberInDifferentCultures(string cultu
}
}

[Test]
public void CalendarSerialization_ShouldDefaultTo_Utf8NoBom()
{
var calendar = new Calendar();
calendar.Events.Add(new CalendarEvent
{
Summary = "Sample Event",
DtStart = new CalDateTime(2025, 6, 10, 9, 0, 0),
DtEnd = new CalDateTime(2025, 6, 10, 10, 0, 0)
});

// Serialize with default encoding (should be UTF-8 without BOM)
using var msNoBom = new MemoryStream();
new CalendarSerializer().Serialize(calendar, msNoBom);
var noBomBytes = msNoBom.ToArray();

// Serialize with explicit UTF-8 BOM
using var msWithBom = new MemoryStream();
new CalendarSerializer().Serialize(calendar, msWithBom, new UTF8Encoding(true));
var withBomBytes = msWithBom.ToArray();

// UTF-8 BOM is 0xEF,0xBB,0xBF
bool HasBom(byte[] bytes) =>
bytes.Length >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF;

Assert.Multiple(() =>
{
Assert.That(HasBom(noBomBytes), Is.False, "Stream should not contain a UTF-8 BOM");
Assert.That(HasBom(withBomBytes), Is.True, "Stream should contain a UTF-8 BOM");
});
}
}
2 changes: 1 addition & 1 deletion Ical.Net/Serialization/ISerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ public interface ISerializer : IServiceProvider
SerializationContext SerializationContext { get; set; }

Type TargetType { get; }
void Serialize(object obj, Stream stream, Encoding encoding);
void Serialize(object obj, Stream stream, Encoding? encoding = null);
object? Deserialize(Stream stream, Encoding encoding);
}
25 changes: 20 additions & 5 deletions Ical.Net/Serialization/SerializerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,28 @@ protected SerializerBase(SerializationContext ctx)
return obj;
}

public void Serialize(object obj, Stream stream, Encoding encoding)
/// <summary>
/// Serializes the specified object to the provided stream using the specified encoding.
/// </summary>
/// <remarks>This method writes the serialized representation of the object to the stream without closing
/// the stream, allowing the caller to continue using it.
/// bytes.</remarks>
/// <param name="obj">The object to serialize. Must not be null.</param>
/// <param name="stream">The stream to which the serialized data will be written. Must be writable.</param>
/// <param name="encoding">
/// The character encoding to use for serialization.
/// If <see langword="null"/> or missing, UTF-8 encoding
/// without a byte order mark (BOM) is used.
/// A BOM is incompatible with many iCalendar apps.
/// </param>
public void Serialize(object obj, Stream stream, Encoding? encoding = null)
{
// NOTE: we don't use a 'using' statement here because
// we don't want the stream to be closed by this serialization.
// Fixes bug #3177278 - Serialize closes stream
// Ensure that no BOM is written to the stream
encoding ??= new UTF8Encoding(false);

const int defaultBuffer = 1024; //This is StreamWriter's built-in default buffer size
//This is StreamWriter's built-in default buffer size
const int defaultBuffer = 1024;
// Important: leave the stream open so that the caller can continue to use it
using var sw = new StreamWriter(stream, encoding, defaultBuffer, leaveOpen: true);

// Push the current object onto the serialization stack
Expand Down
Loading