CSharp · Console
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Xml.Serialization;
using System.Xml;
using System.Xml.Linq;
using System.IO;
using System.Data;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO.Compression;
using System.Reflection;
using System.Threading.Tasks;
using System.Threading;
using NUnit.Framework; //NUnit.Framework.AssertionException
//https://stackoverflow.com/questions/61185023/pass-a-char-buffer-to-a-xmlserializer
public sealed class CharMemoryReader : TextReader
{
private ReadOnlyMemory<char> chars;
private int position;
public CharMemoryReader(ReadOnlyMemory<char> chars)
{
this.chars = chars;
this.position = 0;
}
void CheckClosed()
{
if (position < 0)
throw new ObjectDisposedException(null, string.Format("{0} is closed.", ToString()));
}
public override void Close() => Dispose(true);
protected override void Dispose(bool disposing)
{
chars = ReadOnlyMemory<char>.Empty;
position = -1;
base.Dispose(disposing);
}
public override int Peek()
{
CheckClosed();
return position >= chars.Length ? -1 : chars.Span[position];
}
public override int Read()
{
CheckClosed();
return position >= chars.Length ? -1 : chars.Span[position++];
}
public override int Read(char[] buffer, int index, int count)
{
CheckClosed();
if (buffer == null)
throw new ArgumentNullException(nameof(buffer));
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index));
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count));
if (buffer.Length - index < count)
throw new ArgumentException("buffer.Length - index < count");
return Read(buffer.AsSpan().Slice(index, count));
}
public override int Read(Span<char> buffer)
{
CheckClosed();
var nRead = chars.Length - position;
if (nRead > 0)
{
if (nRead > buffer.Length)
nRead = buffer.Length;
chars.Span.Slice(position, nRead).CopyTo(buffer);
position += nRead;
}
return nRead;
}
public override string ReadToEnd()
{
CheckClosed();
var s = position == 0 ? chars.ToString() : chars.Slice(position, chars.Length - position).ToString();
position = chars.Length;
return s;
}
public override string ReadLine()
{
CheckClosed();
var span = chars.Span;
var i = position;
for( ; i < span.Length; i++)
{
var ch = span[i];
if (ch == '\r' || ch == '\n')
{
var result = span.Slice(position, i - position).ToString();
position = i + 1;
if (ch == '\r' && position < span.Length && span[position] == '\n')
position++;
return result;
}
}
if (i > position)
{
var result = span.Slice(position, i - position).ToString();
position = i;
return result;
}
return null;
}
public override int ReadBlock(char[] buffer, int index, int count) => Read(buffer, index, count);
public override int ReadBlock(Span<char> buffer) => Read(buffer);
public override Task<String> ReadLineAsync() => Task.FromResult(ReadLine());
public override Task<String> ReadToEndAsync() => Task.FromResult(ReadToEnd());
public override Task<int> ReadBlockAsync(char[] buffer, int index, int count) => Task.FromResult(ReadBlock(buffer, index, count));
public override Task<int> ReadAsync(char[] buffer, int index, int count) => Task.FromResult(Read(buffer, index, count));
public override ValueTask<int> ReadBlockAsync(Memory<char> buffer, CancellationToken cancellationToken = default) =>
cancellationToken.IsCancellationRequested ? new ValueTask<int>(Task.FromCanceled<int>(cancellationToken)) : new ValueTask<int>(ReadBlock(buffer.Span));
public override ValueTask<int> ReadAsync(Memory<char> buffer, CancellationToken cancellationToken = default) =>
cancellationToken.IsCancellationRequested ? new ValueTask<int>(Task.FromCanceled<int>(cancellationToken)) : new ValueTask<int>(Read(buffer.Span));
}
public class MyEntity
{
public List<string> Strings { get; } = new List<string>();
}
public class TestClass
{
public static void Test()
{
Test("ぁあぃいぅうぇえぉおかがきぎく", "\n\nぁあぃいぅうぇえぉおかがきぎく\U00029E3D\u0302");
BasicTests("aaaa");
BasicTests("\n\r\n\n\r\nぁあぃいぅうぇえぉおかがきぎく\U00029E3D\u0302");
BasicTests("ぁあぃいぅうぇえぉおかがきぎく\U00029E3D\u0302");
}
public static void Test(params string [] strings)
{
var root = new MyEntity();
root.Strings.AddRange(strings);
var _xmlSerializer = new XmlSerializer(typeof(MyEntity));
var xml = root.GetXml(_xmlSerializer);
Console.WriteLine(xml);
var buffer = xml.ToArray();
int contentLength = buffer.Length;
var result = buffer.LoadFromXml<MyEntity>(contentLength, _xmlSerializer);
Console.WriteLine("Re-serialized {0}:", result);
Console.WriteLine(result.GetXml());
Assert.IsTrue(result.Strings.SequenceEqual(root.Strings));
BasicTests(xml);
}
static void BasicTests(string s)
{
var array = s.ToArray();
using (var reader = new CharMemoryReader(array.AsMemory()))
{
Assert.AreEqual(s, reader.ReadToEnd(), "s");
}
using (var reader = new CharMemoryReader(s.AsMemory()))
{
// Ensure that all the slicing & spanning returns the original instance of the string.
Assert.IsTrue(object.ReferenceEquals(s, reader.ReadToEnd()));
}
using (var reader = new CharMemoryReader(array.AsMemory()))
using (var stringReader = new StringReader(s))
{
Assert.IsTrue(reader.ReadAllLines().SequenceEqual(stringReader.ReadAllLines()));
}
using (var reader = new CharMemoryReader(array.AsMemory()))
using (var stringReader = new StringReader(s))
{
Assert.IsTrue(reader.ReadAllChars().SequenceEqual(stringReader.ReadAllChars()));
}
using (var reader = new CharMemoryReader(array.AsMemory()))
{
var buffer = new Span<char>(new char [1000]);
int i = 0;
int n = 0;
while ((n = reader.Read(buffer)) > 0)
{
Assert.IsTrue(s.AsSpan(i, n).Equals(buffer.Slice(0, n), StringComparison.Ordinal));
i += n;
}
}
}
}
static class TextReaderExtensions
{
public static IEnumerable<string> ReadAllLines(this TextReader reader)
{
string l;
while ((l = reader.ReadLine()) != null)
yield return l;
}
public static IEnumerable<char> ReadAllChars(this TextReader reader)
{
int ch;
while ((ch = reader.Read()) >= 0)
yield return (char)ch;
}
}
public static partial class XmlSerializationHelper
{
public static T LoadFromXml<T>(this char [] xml, int contentLength, XmlSerializer serial = null) =>
new ReadOnlyMemory<char>(xml, 0, contentLength).LoadFromXml<T>(serial);
public static T LoadFromXml<T>(this ReadOnlyMemory<char> xml, XmlSerializer serial = null)
{
serial = serial ?? new XmlSerializer(typeof(T));
using (var reader = new CharMemoryReader(xml))
return (T)serial.Deserialize(reader);
}
}
public static partial class XmlSerializationHelper
{
public static T LoadFromXml<T>(this string xmlString, XmlSerializer serial = null)
{
serial = serial ?? new XmlSerializer(typeof(T));
using (var reader = new StringReader(xmlString))
{
return (T)serial.Deserialize(reader);
}
}
public static string GetXml<T>(this T obj, XmlSerializer serializer = null, bool omitStandardNamespaces = false)
{
XmlSerializerNamespaces ns = null;
if (omitStandardNamespaces)
{
ns = new XmlSerializerNamespaces();
ns.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
}
using (var textWriter = new StringWriter())
{
var settings = new XmlWriterSettings() { Indent = true }; // For cosmetic purposes.
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
(serializer ?? new XmlSerializer(obj.GetType())).Serialize(xmlWriter, obj, ns);
return textWriter.ToString();
}
}
}
public class Program
{
public static void Main()
{
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine();
try
{
TestClass.Test();
}
catch (Exception ex)
{
Console.WriteLine("Failed with unhandled exception: ");
Console.WriteLine(ex);
throw;
}
}
public static string GetNetCoreVersion()
{
//https://techblog.dorogin.com/how-to-detect-net-core-version-in-runtime-ecd65ad695be
//
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];
return null;
}
}