157 строки
5.5 KiB
C#
157 строки
5.5 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net.Http;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Xml;
|
|
using System.Xml.Serialization;
|
|
|
|
namespace Refit
|
|
{
|
|
|
|
public class XmlContentSerializer : IHttpContentSerializer
|
|
{
|
|
readonly XmlContentSerializerSettings settings;
|
|
readonly ConcurrentDictionary<Type, XmlSerializer> serializerCache = new();
|
|
|
|
public XmlContentSerializer() : this(new XmlContentSerializerSettings())
|
|
{
|
|
}
|
|
|
|
public XmlContentSerializer(XmlContentSerializerSettings settings)
|
|
{
|
|
this.settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
|
}
|
|
|
|
public HttpContent ToHttpContent<T>(T item)
|
|
{
|
|
if (item is null) throw new ArgumentNullException(nameof(item));
|
|
|
|
var xmlSerializer = serializerCache.GetOrAdd(item.GetType(), t => new XmlSerializer(t, settings.XmlAttributeOverrides));
|
|
|
|
using var stream = new MemoryStream();
|
|
using var writer = XmlWriter.Create(stream, settings.XmlReaderWriterSettings.WriterSettings);
|
|
var encoding = settings.XmlReaderWriterSettings.WriterSettings?.Encoding ?? Encoding.Unicode;
|
|
xmlSerializer.Serialize(writer, item, settings.XmlNamespaces);
|
|
var str = encoding.GetString(stream.ToArray());
|
|
var content = new StringContent(str, encoding, "application/xml");
|
|
return content;
|
|
}
|
|
|
|
public async Task<T?> FromHttpContentAsync<T>(HttpContent content, CancellationToken cancellationToken = default)
|
|
{
|
|
var xmlSerializer = serializerCache.GetOrAdd(typeof(T), t => new XmlSerializer(
|
|
t,
|
|
settings.XmlAttributeOverrides,
|
|
Array.Empty<Type>(),
|
|
null,
|
|
settings.XmlDefaultNamespace));
|
|
|
|
|
|
using var input = new StringReader(await content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false));
|
|
|
|
using var reader = XmlReader.Create(input, settings.XmlReaderWriterSettings.ReaderSettings);
|
|
return (T?)xmlSerializer.Deserialize(reader);
|
|
}
|
|
|
|
public string? GetFieldNameForProperty(PropertyInfo propertyInfo)
|
|
{
|
|
if (propertyInfo is null)
|
|
throw new ArgumentNullException(nameof(propertyInfo));
|
|
|
|
return propertyInfo.GetCustomAttributes<XmlElementAttribute>(true)
|
|
.Select(a => a.ElementName)
|
|
.FirstOrDefault()
|
|
??
|
|
propertyInfo.GetCustomAttributes<XmlAttributeAttribute>(true)
|
|
.Select(a => a.AttributeName)
|
|
.FirstOrDefault();
|
|
}
|
|
}
|
|
|
|
public class XmlReaderWriterSettings
|
|
{
|
|
XmlReaderSettings readerSettings;
|
|
XmlWriterSettings writerSettings;
|
|
|
|
public XmlReaderWriterSettings() : this(new XmlReaderSettings(), new XmlWriterSettings())
|
|
{
|
|
}
|
|
|
|
public XmlReaderWriterSettings(XmlReaderSettings readerSettings) : this(readerSettings, new XmlWriterSettings())
|
|
{
|
|
}
|
|
|
|
public XmlReaderWriterSettings(XmlWriterSettings writerSettings) : this(new XmlReaderSettings(), writerSettings)
|
|
{
|
|
}
|
|
|
|
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
|
public XmlReaderWriterSettings(XmlReaderSettings readerSettings, XmlWriterSettings writerSettings)
|
|
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
|
{
|
|
ReaderSettings = readerSettings;
|
|
WriterSettings = writerSettings;
|
|
}
|
|
|
|
public XmlReaderSettings ReaderSettings
|
|
{
|
|
get
|
|
{
|
|
ApplyOverrideSettings();
|
|
return readerSettings;
|
|
}
|
|
set => readerSettings = value ?? throw new ArgumentNullException(nameof(value));
|
|
}
|
|
|
|
public XmlWriterSettings WriterSettings
|
|
{
|
|
get
|
|
{
|
|
ApplyOverrideSettings();
|
|
return writerSettings;
|
|
}
|
|
set => writerSettings = value ?? throw new ArgumentNullException(nameof(value));
|
|
}
|
|
|
|
/// <summary>
|
|
/// The writer and reader settings are set by the caller, but certain properties
|
|
/// should remain set to meet the demands of the XmlContentSerializer. Those properties
|
|
/// are always set here.
|
|
/// </summary>
|
|
void ApplyOverrideSettings()
|
|
{
|
|
writerSettings.Async = true;
|
|
readerSettings.Async = true;
|
|
}
|
|
}
|
|
|
|
public class XmlContentSerializerSettings
|
|
{
|
|
public XmlContentSerializerSettings()
|
|
{
|
|
XmlDefaultNamespace = null;
|
|
XmlReaderWriterSettings = new XmlReaderWriterSettings();
|
|
XmlNamespaces = new XmlSerializerNamespaces(
|
|
new[]
|
|
{
|
|
new XmlQualifiedName(string.Empty, string.Empty),
|
|
});
|
|
|
|
XmlAttributeOverrides = new XmlAttributeOverrides();
|
|
}
|
|
|
|
public string? XmlDefaultNamespace { get; set; }
|
|
|
|
public XmlReaderWriterSettings XmlReaderWriterSettings { get; set; }
|
|
|
|
public XmlSerializerNamespaces XmlNamespaces { get; set; }
|
|
|
|
public XmlAttributeOverrides XmlAttributeOverrides { get; set; }
|
|
}
|
|
}
|