Preliminary note
For concision, "Summer.Batch." expression may be abbreviated on some occasions using the "S.B." expression, when writing fully qualified names of classes or interfaces;
e.g.:
Summer.Batch.Infrastructure.Item.File.FlatFileItemReader<T>
may be abbreviated as
S.B.Infrastructure.Item.File.FlatFileItemReader<T>
Reading and writing flat files¶
Reading and writing flat files is a very common batch; flat files are still largely used to share information between components of the same system or to integrate data coming from outer systems.
Using a flat file reader¶
A flat file reader implementation is directly available within Summer Batch, and covers typical needs. The class to use is Summer.Batch.Infrastructure.Item.File.FlatFileItemReader<T>
. The template object T represents business object that will be filled by the records read from flat file. Consequently, some mapping has to be done between the records and their properties of target business object: this is achieved using a line mapper that must be provided at initialization time. A line mapper is a class implementing Summer.Batch.Infrastructure.Item.File.ILineMapper<out T>
interface.
A line mapper should implement
T MapLine(string line, int lineNumber)
A default implementation is provided :
Summer.Batch.Infrastructure.Item.File.Mapping.DefaultLineMapper<T>
The mapping is done in a two phases process:
-
Read line from the flat file is split into fields, using a tokenizer that must be specified at initialization time ( class that implements
Summer.Batch.Infrastructure.Item.File.Transform.ILineTokenizer
interface ). Two implementations are being provided to cover the most typical needs:-
Summer.Batch.Infrastructure.Item.File.Transform.FixedLengthTokenizer
: for lines with a fixed-length format. The fields are being specified using ranges (seeSummer.Batch.Infrastructure.Item.File.Transform.Range
); -
Summer.Batch.Infrastructure.Item.File.Transform.DelimitedLineTokenizer
: for lines holding separated fields, e.g. CSV files (the separator string is configurable and defaults to comma).
-
-
The result of first phase is a field set.
Note
see
Summer.Batch.Infrastructure.Item.File.Transform.IFieldSet
interface and default implementation
Summer.Batch.Infrastructure.Item.File.Transform.DefaultFieldSet
Its fields will be mapped to a business object properties using a field set mapper ( class that implements Summer.Batch.Infrastructure.Item.File.Mapping.IFieldSetMapper
interface).
Field set mappers are bound to your business model; each target business object (intended to be filled by records read from flat file) should have an available mapper.
Now let's see a sample. First, the XML job configuration.
Example 6.1. FlatFileItemReader
declaration in the job XML file¶
<step id="FlatFileReader">
<chunk item-count="1000">
<reader ref="FlatFileReader/FlatFileReader" />
...
</chunk>flat file writer
</step>
FlatFileBO
business object. Here is sample flat file data, which we'll be using:
Example 6.2. Sample delimited flat file data¶
1;FlatFile1 ; FlatFile1 ;20100101
2;FlatFile2 ; FlatFile2 ;20100101
Example 6.3. Sample flat file target business object¶
using System;
namespace Com.Netfective.Bluage.Business.Batch.Flatfile.Bos
{
/// <summary>
/// Entity FlatFileBO.
/// </summary>
[Serializable]
public class FlatFileBO
{
/// <summary>
/// Property Code.
/// </summary>
public int? Code { get; set; }
/// <summary>
/// Property Name.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Property Description.
/// </summary>
public string Description { get; set; }
/// <summary>
/// Property Date.
/// </summary>
public DateTime? Date { get; set; }
}
}
IFieldSetMapper
Example 6.4. Sample flat file target business object field set mapper¶
using Summer.Batch.Extra;
using Summer.Batch.Infrastructure.Item.File.Mapping;
using Summer.Batch.Infrastructure.Item.File.Transform;
namespace Com.Netfective.Bluage.Business.Batch.Flatfile.Bos.Mappers
{
/// <summary>
/// Implementation of <see cref="IFieldSetMapper{T}" /> that creates
/// instances of <see cref="FlatFileBO" />.
/// </summary>
public class FlatFileMapper : IFieldSetMapper<FlatFileBO>
{
private IDateParser _dateParser = new DateParser();
/// <summary>
/// Parser for date columns.
/// </summary>
private IDateParser DateParser { set { _dateParser = value; } }
/// <summary>
/// Maps a <see cref="IFieldSet"/> to a <see cref="FlatFileBO" />.
/// <param name="fieldSet">the field set to map</param>
/// <returns>the corresponding item</returns>
/// </summary>
public FlatFileBO MapFieldSet(IFieldSet fieldSet)
{
// Create a new instance of the current mapped object
return new FlatFileBO
{
Code = fieldSet.ReadInt(0),
Name = fieldSet.ReadRawString(1),
Description = fieldSet.ReadRawString(2),
Date = _dateParser.Decode(fieldSet.ReadString(3)),
};
}
}
}
Note
Note the use of a dedicated helper (Summer.Batch.Extra.IDateParser
) to handle DateTime. By default Summer.Batch.Extra.DateParser
is provided, but you can provide your own implementation to cover more specific needs. Please review API doc to see what services are provided by DateParser
.
Now that all bricks are set, let's build the unity configuration.
Example 6.5. Delimited flat file reader - sample unity configuration¶
/// <summary>
/// Registers the artifacts required for step FlatFileReader.
/// </summary>
/// <param name="container">the unity container to use for registrations</param>
private void RegisterFlatFileReader(IUnityContainer container)
{
// Reader - FlatFileReader/FlatFileReader
container.StepScopeRegistration<IItemReader<FlatFileBO>,
FlatFileItemReader<FlatFileBO>>("FlatFileReader/FlatFileReader")
.Property("Resource")
.Resource("#{settings['BA_FLATFILE_READER.FlatFileReader.FILENAME_IN']}")
.Property("Encoding").Value(Encoding.GetEncoding("UTF-8"))
.Property("LineMapper")
.Reference<ILineMapper<FlatFileBO>>("FlatFileReader/FlatFileReader/LineMapper")
.Register();
// Line mapper
container.StepScopeRegistration<ILineMapper<FlatFileBO>,
DefaultLineMapper<FlatFileBO>>("FlatFileReader/FlatFileReader/LineMapper")
.Property("Tokenizer")
.Reference<ILineTokenizer>("FlatFileReader/FlatFileReader/Tokenizer")
.Property("FieldSetMapper")
.Reference<IFieldSetMapper<FlatFileBO>>
("FlatFileReader/FlatFileReader/FieldSetMapper")
.Register();
// Tokenizer
container
.StepScopeRegistration<ILineTokenizer,
DelimitedLineTokenizer>("FlatFileReader/FlatFileReader/Tokenizer")
.Property("Delimiter").Value(";")
.Register();
// Field set mapper
container
.StepScopeRegistration<IFieldSetMapper<FlatFileBO>,
FlatFileMapper>("FlatFileReader/FlatFileReader/FieldSetMapper")
.Register();
// ... -- processor and writer registration is not being shown here --
}
Note
- All registrations within unity container are made using Step Scope (
container.StepScopeRegistration
); - In addition to the mandatory
LineMapper
property,FlatFileItemReader
uses :- A resource to be read (= the flat file); In the sample, resource path is read from the Settings.config file using a key;
- To specify flat file encoding; Use the
Encoding
.GetEncoding
static methods family to provide a proper encoding (Optional); - Other optional properties not shown in the sample :
LinesToSkip
: given number of lines will be skipped at the start of resource;Strict
: flag for the strict mode; in strict mode, an exception will be thrown if specified resource does not exist (vs. a simple warn logged in non-strict mode);
Using a flat file writer¶
Provided implementation is in
Summer.Batch.Infrastructure.Item.File.FlatFileItemWriter<T>
class, where T
is the type of business object that will be "dumped" into flat file. FlatFileItemWriter
uses the following properties:
-
Mandatory properties (to be set at initialization time):
-
Resource
: resource to be written to; -
LineAggregator
: a class implementingSummer.Batch.Infrastructure.Item.File.Transform.ILineAggregator<in T>
interface; this class is responsible for aggregating the business object properties into a single string that can be used to write a line into target flat file.
-
-
Optional properties (some having default values):
-
LineSeparator
: line separator for the lines to write in flat file; defaults to System.Environment.NewLine; -
Transactional
: a flag to specify if the writer should take part in the active transaction (meaning that data will be effectively written at commit time); defaults to true; -
AutoFlush
: a flag to specify if the writer buffer should be flushed after each write; defaults to false; -
SaveState
: a flag to specify if the state of the item writer should be saved in the execution context when Update method is called; defaults to true; -
AppendAllowed
: a flag to specify if an existing resource should be written to in append mode; defaults to false; -
DeleteIfExists
: a flag to specify if an existing resource should be deleted; if AppendAllowed is set to true, this flag is IGNORED; defaults to false; -
DeleteIfEmpty
: a flag to specify if an empty target resource (no lines were written) should be deleted; defaults to false; -
HeaderWriter
: a header writer (class implementing theSummer.Batch.Infrastructure.Item.File.IHeaderWriter
interface); Used to write the header of file; No default value; -
FooterWriter
: a footer writer (class implementing theSummer.Batch.Infrastructure.Item.File.IFooterWriter
interface); Used to write the footer of file; No default value;
-
The writing process takes a business object as input, transforms it into a string using LineAggregator
and append the string to target resource.
Now let's review an example; first, the job XML configuration :
Example 6.6. FlatFileItemWriter
declaration in the job XML file¶
<step id="step1">
<chunk item-count="1000">
...
<writer ref="step1/FlatFileWriter" />
</chunk>
</step>
LineAggregator
; Summer Batch comes with several ILineAggregator
implementations :
-
Summer.Batch.Infrastructure.Item.File.Transform.DelimitedLineAggregator<T>
: transforms an object properties into a delimited list of strings. Default delimiter is comma, but can set to any arbitrary string; This is the natural choice to write CSV files; -
Summer.Batch.Infrastructure.Item.File.Transform.FormatterLineAggregator<T>
: transforms an object properties into a string, using a provided format (the computed string is the result of a call tostring.Format
method, using provided format.); -
Summer.Batch.Infrastructure.Item.File.Transform.PassThroughLineAggregator<T>
: transforms an object to a string by simply callingToString
method of the object;
Our example uses FormatterLineAggregator
, providing a format string through unity configuration; To fully understand unity configuration that follows, used business object EmployeeDetailBO
must be shown:
Example 6.7. Sample flat file writer input business object¶
using System;
namespace Com.Netfective.Bluage.Business.Batch.Flatfilewriter.Bo
{
/// <summary>
/// Entity EmployeeDetailBO.
/// </summary>
[Serializable]
public class EmployeeDetailBO
{
/// <summary>
/// Property EmpId.
/// </summary>
public int? EmpId { get; set; }
/// <summary>
/// Property EmpName.
/// </summary>
public string EmpName { get; set; }
/// <summary>
/// Property Name.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Property EmpDob.
/// </summary>
public DateTime? EmpDob { get; set; }
/// <summary>
/// Property EmpSalary.
/// </summary>
public decimal? EmpSalary { get; set; }
/// <summary>
/// Property EmailId.
/// </summary>
public string EmailId { get; set; }
/// <summary>
/// Property BuildingNo.
/// </summary>
public int? BuildingNo { get; set; }
/// <summary>
/// Property StreetName.
/// </summary>
public string StreetName { get; set; }
/// <summary>
/// Property City.
/// </summary>
public string City { get; set; }
/// <summary>
/// Property State.
/// </summary>
public string State { get; set; }
}
}
Example 6.8. Formatted flat file writer - sample unity configuration¶
/// <summary>
/// Registers the artifacts required for step step1.
/// </summary>
/// <param name="container">the unity container to use for registrations</param>
private void RegisterStep1(IUnityContainer container)
{
//... -- reader and processor registration is not being shown here --
// step1/FlatFileListWriter/Delegate Writer
container.StepScopeRegistration<IItemWriter<EmployeeDetailBO>,
FlatFileItemWriter<EmployeeDetailBO>>("step1/FlatFileWriter")
.Property("Resource")
.Resource("#{settings['BA_FLAT_FILE_WRITER.step1.FlatFileWriter.FILENAME_OUT']}")
.Property("Encoding").Value(Encoding.GetEncoding("UTF-8"))
.Property("LineAggregator")
.Reference<ILineAggregator<EmployeeDetailBO>>("step1/FlatFileWriter/LineAggregator")
.Register();
// Line aggregator
container.StepScopeRegistration<ILineAggregator<EmployeeDetailBO>,
FormatterLineAggregator<EmployeeDetailBO>>("step1/FlatFileWriter/LineAggregator")
.Property("Format")
.Value("{0},{1},{2},{3:yyyy-MM-dd},{4},{5},{6},{7},{8},{9}")
.Property("FieldExtractor")
.Reference<IFieldExtractor<EmployeeDetailBO>>("step1/FlatFileWriter/FieldsExtractor")
.Register();
// Fields Extractor
container.StepScopeRegistration<IFieldExtractor<EmployeeDetailBO>,
PropertyFieldExtractor<EmployeeDetailBO>>("step1/FlatFileWriter/FieldsExtractor")
.Property("Names").LateBinding<string[]>("EmpId,EmpName,Name,EmpDob,EmpSalary," +
"EmailId,BuildingNo,StreetName,City,State")
.Register();
}
Note
-
FormatterLineAggregator
requires aSummer.Batch.Infrastructure.Item.File.Transform.IFieldExtractor<in T>
implementation at initialization time; TheIFieldExtractor
is in charge of converting a business object into an array of its parts (array of values, build using the object properties); Summer Batch provides several implementations:-
S.B.Infrastructure.Item.File.Transform.PropertyFieldExtractor<T>
: this is the implementation being used in the example; it retrieves values from property names (usingNames
property); Examining the line :we see that all the.Property("Names").LateBinding<string[]>("EmpId,EmpName,Name,EmpDob,EmpSalary," +"EmailId,BuildingNo,StreetName,City,State")
EmployeeDetailBO
properties are being selected to be written to target flat file; The order is significant.These properties values will be passed in that order to
string.Format
method which is used byFormattedLineAggregator
. The format being usedindicates that all selected properties will be effectively written to targeted flat file..Property("Format").Value("{0},{1},{2},{3:yyyy-MM-dd},{4},{5},{6},{7},{8},{9}")
-
S.B.Infrastructure.Item.File.Transform.PassThroughFieldExtractor<object>
: this implementation returns the business object as an array; see api doc for details;
-