[Fork] https://stackoverflow.com/questions/61185023/pass-a-char-buffer-to-a-xmlserializer

CSharp · Console

/Program.cs

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;
		}
	}
;