-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added support for CSV serialization to the main library
- Loading branch information
1 parent
5e28213
commit 9e1694f
Showing
11 changed files
with
618 additions
and
0 deletions.
There are no files selected for viewing
98 changes: 98 additions & 0 deletions
98
src/UXI.GazeToolkit.Serialization/Formats/Csv/CsvDataReader.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using CsvHelper; | ||
using UXI.GazeToolkit.Serialization.Common; | ||
using UXI.GazeToolkit.Serialization.Csv.DataConverters; | ||
|
||
namespace UXI.GazeToolkit.Serialization.Csv | ||
{ | ||
public class CsvDataReader : DisposableBase, IDataReader, IDisposable | ||
{ | ||
private readonly CsvReader _reader; | ||
private readonly CsvSerializerContext _serializer; | ||
private readonly IDataConverter _converter; | ||
private bool _shouldReadHeader = true; | ||
|
||
public CsvDataReader(TextReader reader, Type dataType, CsvSerializerContext serializer) | ||
{ | ||
_reader = new CsvReader(reader, serializer.Configuration); | ||
_serializer = serializer; | ||
|
||
_converter = serializer.DataConverters.FirstOrDefault(c => c.CanConvert(dataType)); // TODO ?? DefaultCsvConverter ? | ||
|
||
DataType = dataType; | ||
} | ||
|
||
|
||
public Type DataType { get; } | ||
|
||
// TODO not in converters, will never work if object of other type is passed because no converter will be found | ||
public bool CanRead(Type objectType) | ||
{ | ||
return DataType == objectType | ||
|| DataType.IsSubclassOf(objectType) | ||
|| objectType.IsAssignableFrom(DataType); | ||
} | ||
|
||
|
||
public bool TryRead(out object data) | ||
{ | ||
TryReadHeader(); | ||
|
||
try | ||
{ | ||
if (_reader.Read()) | ||
{ | ||
data = _converter.ReadCsv(_reader, DataType, _serializer); | ||
return true; | ||
} | ||
} | ||
catch (Exception exception) | ||
{ | ||
throw new System.Runtime.Serialization.SerializationException("Failed to read or deserialize next data from the CSV file. See inner exception for more details.", exception); | ||
} | ||
|
||
data = null; | ||
return false; | ||
} | ||
|
||
|
||
private void TryReadHeader() | ||
{ | ||
if (_shouldReadHeader) | ||
{ | ||
try | ||
{ | ||
if (_reader.Read()) | ||
{ | ||
_converter.ReadCsvHeader(_reader, _serializer); | ||
_shouldReadHeader = false; | ||
} | ||
} | ||
catch (CsvHelperException exception) | ||
{ | ||
throw new System.Runtime.Serialization.SerializationException("Failed to read CSV file header. See inner exception for more details.", exception); | ||
} | ||
} | ||
} | ||
|
||
|
||
private bool _disposed = false; | ||
protected override void Dispose(bool disposing) | ||
{ | ||
if (_disposed == false) | ||
{ | ||
if (disposing) | ||
{ | ||
_reader.Dispose(); | ||
} | ||
_disposed = true; | ||
} | ||
base.Dispose(disposing); | ||
} | ||
} | ||
} |
100 changes: 100 additions & 0 deletions
100
src/UXI.GazeToolkit.Serialization/Formats/Csv/CsvDataWriter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Runtime.Serialization; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using CsvHelper; | ||
using UXI.GazeToolkit.Serialization.Common; | ||
using UXI.GazeToolkit.Serialization.Csv.DataConverters; | ||
|
||
namespace UXI.GazeToolkit.Serialization.Csv | ||
{ | ||
public class CsvDataWriter : DisposableBase, IDataWriter, IDisposable | ||
{ | ||
private readonly CsvWriter _writer; | ||
private readonly CsvSerializerContext _serializer; | ||
private readonly IDataConverter _converter; | ||
private bool _isOpen = true; | ||
|
||
private bool _shouldWriteHeader = true; | ||
|
||
public CsvDataWriter(TextWriter writer, Type dataType, CsvSerializerContext serializer) | ||
{ | ||
_writer = new CsvWriter(writer, serializer.Configuration); | ||
|
||
_serializer = serializer; | ||
|
||
_converter = serializer.DataConverters.FirstOrDefault(c => c.CanConvert(dataType)); | ||
|
||
DataType = dataType; | ||
} | ||
|
||
|
||
public Type DataType { get; } | ||
|
||
|
||
public bool CanWrite(Type objectType) | ||
{ | ||
return _isOpen | ||
&& (DataType == objectType | ||
|| objectType.IsSubclassOf(DataType) | ||
|| DataType.IsAssignableFrom(objectType)); | ||
} | ||
|
||
|
||
public void Write(object data) | ||
{ | ||
TryWriteHeader(); | ||
|
||
if (_isOpen) | ||
{ | ||
try | ||
{ | ||
_converter.WriteCsv(data, _writer, _serializer); | ||
} | ||
catch (Exception exception) | ||
{ | ||
throw new SerializationException($"Failed to write or serialize next data to the CSV file. See inner exception for more details.", exception); | ||
} | ||
} | ||
} | ||
|
||
|
||
private void TryWriteHeader() | ||
{ | ||
if (_shouldWriteHeader) | ||
{ | ||
_converter.WriteCsvHeader(_writer, DataType, _serializer); | ||
_shouldWriteHeader = false; | ||
} | ||
} | ||
|
||
|
||
public void Close() | ||
{ | ||
if (_isOpen) | ||
{ | ||
TryWriteHeader(); | ||
_isOpen = false; | ||
_writer.Flush(); | ||
} | ||
} | ||
|
||
|
||
private bool _disposed = false; | ||
protected override void Dispose(bool disposing) | ||
{ | ||
if (_disposed == false) | ||
{ | ||
if (disposing) | ||
{ | ||
_writer.Dispose(); | ||
} | ||
_disposed = true; | ||
} | ||
base.Dispose(disposing); | ||
} | ||
} | ||
} |
129 changes: 129 additions & 0 deletions
129
src/UXI.GazeToolkit.Serialization/Formats/Csv/CsvSerializationFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
using CsvHelper; | ||
using CsvHelper.TypeConversion; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.ObjectModel; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using UXI.GazeToolkit.Serialization.Converters; | ||
using UXI.GazeToolkit.Serialization.Csv.DataConverters; | ||
using UXI.GazeToolkit.Serialization.Csv.TypeConverters; | ||
|
||
namespace UXI.GazeToolkit.Serialization.Csv | ||
{ | ||
public class CsvSerializationFactory : IDataSerializationFactory | ||
{ | ||
public CsvSerializationFactory() { } | ||
|
||
public CsvSerializationFactory(IDictionary<Type, ITypeConverter> typeConverters, bool replaceDefaultConverters, params IDataConverter[] converters) | ||
: this(typeConverters, replaceDefaultConverters, converters.AsEnumerable()) | ||
{ } | ||
|
||
public CsvSerializationFactory(params IDataConverter[] converters) | ||
: this(null, false, converters.AsEnumerable()) | ||
{ } | ||
|
||
public CsvSerializationFactory(bool replaceDefaultConverters, params IDataConverter[] converters) | ||
: this(null, replaceDefaultConverters, converters.AsEnumerable()) | ||
{ } | ||
|
||
public CsvSerializationFactory(IDictionary<Type, ITypeConverter> typeConverters, bool replaceDefaultConverters, IEnumerable<IDataConverter> converters) | ||
{ | ||
if (typeConverters != null) | ||
{ | ||
foreach (var typeConverterPair in typeConverters) | ||
{ | ||
if (DefaultTypeConverters.ContainsKey(typeConverterPair.Key)) | ||
{ | ||
DefaultTypeConverters[typeConverterPair.Key] = typeConverterPair.Value; | ||
} | ||
else | ||
{ | ||
DefaultTypeConverters.Add(typeConverterPair.Key, typeConverterPair.Value); | ||
} | ||
} | ||
} | ||
|
||
if (replaceDefaultConverters) | ||
{ | ||
DefaultConverters.Clear(); | ||
} | ||
|
||
if (converters != null) | ||
{ | ||
DefaultConverters.AddRange(converters); | ||
} | ||
} | ||
|
||
|
||
public FileFormat Format => FileFormat.CSV; | ||
|
||
|
||
public Dictionary<Type, ITypeConverter> DefaultTypeConverters { get; } = new Dictionary<Type, ITypeConverter>(); | ||
|
||
|
||
public List<IDataConverter> DefaultConverters { get; } = new List<IDataConverter>() | ||
{ | ||
new DefaultDataConverter() | ||
}; | ||
|
||
|
||
public IDataReader CreateReaderForType(TextReader reader, Type dataType, SerializationConfiguration configuration) | ||
{ | ||
var serializer = CreateSerializer(configuration); | ||
|
||
return new CsvDataReader(reader, dataType, serializer); | ||
} | ||
|
||
|
||
public IDataWriter CreateWriterForType(TextWriter writer, Type dataType, SerializationConfiguration configuration) | ||
{ | ||
var serializer = CreateSerializer(configuration); | ||
|
||
return new CsvDataWriter(writer, dataType, serializer); | ||
} | ||
|
||
|
||
private CsvSerializerContext CreateSerializer(SerializationConfiguration configuration) | ||
{ | ||
var serializer = new CsvSerializerContext(); | ||
|
||
serializer.Configuration.PrepareHeaderForMatch = header => header.ToLower(); | ||
|
||
SetupDateTimeOffsetSerialization(serializer, configuration.TimestampConverter); | ||
SetupTimestampedDataSerialization(serializer, configuration.TimestampFieldName); | ||
|
||
SetupDefaultConverters(serializer); | ||
|
||
return serializer; | ||
} | ||
|
||
|
||
private void SetupDateTimeOffsetSerialization(CsvSerializerContext serializer, ITimestampStringConverter timestampConverter) | ||
{ | ||
serializer.Configuration.TypeConverterCache.AddConverter<DateTimeOffset>(new DateTimeOffsetTypeConverter(timestampConverter)); | ||
} | ||
|
||
|
||
private void SetupTimestampedDataSerialization(CsvSerializerContext serializer, string timestampFieldName) | ||
{ | ||
serializer.TimestampFieldName = timestampFieldName; | ||
} | ||
|
||
|
||
private void SetupDefaultConverters(CsvSerializerContext serializer) | ||
{ | ||
foreach (var typeConverter in DefaultTypeConverters) | ||
{ | ||
serializer.Configuration.TypeConverterCache.AddConverter(typeConverter.Key, typeConverter.Value); | ||
} | ||
|
||
foreach (var converter in DefaultConverters) | ||
{ | ||
serializer.DataConverters.Add(converter); | ||
} | ||
} | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
src/UXI.GazeToolkit.Serialization/Formats/Csv/CsvSerializerContext.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.ObjectModel; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using CsvHelper.Configuration; | ||
using UXI.GazeToolkit.Serialization.Converters; | ||
using UXI.GazeToolkit.Serialization.Csv.DataConverters; | ||
|
||
namespace UXI.GazeToolkit.Serialization.Csv | ||
{ | ||
public class CsvSerializerContext | ||
{ | ||
public Configuration Configuration { get; set; } = new CsvHelper.Configuration.Configuration(); | ||
|
||
public Collection<IDataConverter> DataConverters { get; set; } = new Collection<IDataConverter>(); | ||
|
||
public ITimestampStringConverter TimestampConverter { get; set; } | ||
|
||
public string TimestampFieldName { get; set; } | ||
} | ||
} |
Oops, something went wrong.