You are on page 1of 36

Like every other website we use cookies.

By using our site you acknowledge that you


have read and understand our Cookie Policy, Privacy Policy, and our Terms of
Service. Learn more
Ask me later
Allow cookies
Click here to Skip to main content
14,556,277 members
Sign in
Home
Search for articles, questions, tips
Submit
homearticles
Chapters and Sections>
Latest Articles
Top Articles
Posting/Update Guidelines
Article Help Forum
Submit an article or tip
Import GitHub Project
Import your Blog
quick answers
Ask a Question about this article
Ask a Question
View Unanswered Questions
View All Questions
View C# questions
View VB.NET questions
View SQL questions
View ASP.NET questions
View Javascript questions
discussions
All Message Boards...
Application Lifecycle>
Running a Business
Sales / Marketing
Collaboration / Beta Testing
Work Issues
Design and Architecture
ASP.NET
JavaScript
C / C++ / MFC>
ATL / WTL / STL
Managed C++/CLI
C#
Free Tools
Objective-C and Swift
Database
Hardware & Devices>
System Admin
Hosting and Servers
Java
Linux Programming
.NET (Core and Framework)
Android
iOS
Mobile
SharePoint
Silverlight / WPF
Visual Basic
Web Development
Site Bugs / Suggestions
Spam and Abuse Watch
features
Competitions
News
The Insider Newsletter
The Daily Build Newsletter
Newsletter archive
Surveys
Product Showcase
CodeProject Stuff
community
Who's Who
Most Valuable Professionals
The Lounge
Where I Am: Member Photos
The Insider News
The Weird & The Wonderful
help
What is 'CodeProject'?
General FAQ
Ask a Question
Bugs and Suggestions
Article Help Forum
About Us
Articles » Platforms, Frameworks & Libraries » Libraries » General

Cinchoo ETL - JSON Writer

Cinchoo
Rate this:

5.00 (1 vote)
8 Jun 2020
CPOL
Simple JSON writer for .NET
ChoETL is an open source ETL (extract, transform and load) framework for .NET. It
is a code based library for extracting data from multiple sources, transforming,
and loading into your very own data warehouse in .NET environment. You can have
data in your data warehouse in no time.
Download source code
Download binary (.NET Framework)
Download binary (.NET Standard / .NET Core)
Contents
1. Introduction
2. Requirement
3. "Hello World!" Sample
3.1. Quick write - Data First Approach
3.2. Code First Approach
3.3. Configuration First Approach
4. Writing All Records
5. Write Records Manually
6. Customize JSON Record
8. Customize JSON Fields
8.1. DefaultValue
8.2. ChoFallbackValue
8.3. Type Converters
8.3.1. Declarative Approach
8.3.2. Configuration Approach
8.3.3. Custom Value Converter Approach
8.4. Validations
8.5. ChoIgnoreMember
8.6. StringLength
8.6. Display
8.7. DisplayName
10. Callback Mechanism
10.1 Using JSONWriter events
10.2 Implementing IChoNotifyRecordWrite interface
10.1 BeginWrite
10.2 EndWrite
10.3 BeforeRecordWrite
10.4 AfterRecordWrite
10.5 RecordWriteError
10.6 BeforeRecordFieldWrite
10.7 AfterRecordFieldWrite
10.8 RecordWriteFieldError
11. Customization
12. Using Dynamic Object
13. Exceptions
15. Using MetadataType Annotation
16. Configuration Choices
16.1 Manual Configuration
16.2 Auto Map Configuration
16.3 Attaching MetadataType class
17. ToTextAll Helper Method
17a. ToText Helper Method
18. Writing DataReader Helper Method
19. Writing DataTable Helper Method
20. Advanced Topics
20.1 Override Converters Format Specs
20.2 Currency Support
20.3 Enum Support
20.4 Boolean Support
20.5 DateTime Support
21. Fluent API
21.1. NullValueHandling
21.2. Formatting
21.3 WithFields
21.4 WithField
21.5. IgnoreFieldValueMode
21.6 ColumnCountStrict
21.7. Configure
21.8. Setup
1. Introduction
ChoETL is an open source ETL (extract, transform and load) framework for .NET. It
is a code based library for extracting data from multiple sources, transforming,
and loading into your very own data warehouse in .NET environment. You can have
data in your data warehouse in no time.

This article talks about using ChoJSONWriter component offered by ChoETL framework.
It is a simple utility class to save JSON data to a file / external data source.

Corresponding ChoJSONReader, a JSON reader article can be found here.

Features:
Follows JSON standard file rules.
Supports culture specific date, currency and number formats while generating files.
Supports different character encoding.
Provides fine control of date, currency, enum, boolean, number formats when writing
files.
Detailed and robust error handling, allowing you to quickly find and fix problems.
Shorten your development time.
2. Requirement
This framework library is written in C# using .NET 4.5 Framework / .NET core 2.x.

3. "Hello World!" Sample


Open VS.NET 2013 or higher
Create a sample VS.NET (.NET Framework 4.5) Console Application project
Install ChoETL via Package Manager Console using Nuget Command based on working
.NET version:
Install-Package ChoETL.JSON
Install-Package ChoETL.JSON.NETStandard
Use the ChoETL namespace
Let's begin by looking into a simple example of generating the below JSON file
having 2 columns

Listing 3.1 Sample JSON data file (emp.json)

Hide Copy Code


[
{
"Id": 1,
"Name": "Mark"
},
{
"Id": 2,
"Name": "Jason"
}
]
There are number of ways you can get the JSON file be created with minimal setup.

3.1. Quick write - Data First Approach


This is the zero-config and quickest approach to create JSON file in no time. No
typed POCO object is needed. Sample code below shows how to generate sample JSON
file using dynamic objects

Listing 3.1.1 Write list of objects to JSON file

Hide Copy Code


List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();


rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);

using (var parser = new ChoJSONWriter("emp.json"))


{
parser.Write(objs);
}
In the above sample, we give the list of dynamic objects to JSONWriter at one pass
to write them to JSON file.

Listing 3.1.2 Write each object to JSON file

Hide Copy Code


using (var parser = new ChoJSONWriter("emp.json"))
{
dynamic rec1 = new ExpandoObject();
rec1.Id = 1;
rec1.Name = "Mark";
parser.Write(item);

dynamic rec1 = new ExpandoObject();


rec1.Id = 2;
rec1.Name = "Jason";
parser.Write(item);
}
In the above sample, we take control of constructing, passing each and individual
dynamic record to the JSONWriter to generate the JSON file using Write overload.

3.2. Code First Approach


This is another zeo-config way to generate JSON file using typed POCO class. First
define a simple POCO class to match the underlying JSON file layout

Listing 3.2.1 Simple POCO entity class

Hide Copy Code


public partial class EmployeeRecSimple
{
public int Id { get; set; }
public string Name { get; set; }
}
In above, the POCO class defines two properties matching the sample JSON file
template.

Listing 3.2.2 Saving to JSON file

Hide Copy Code


List<EmployeeRecSimple> objs = new List<EmployeeRecSimple>();

EmployeeRecSimple rec1 = new EmployeeRecSimple();


rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);

EmployeeRecSimple rec2 = new EmployeeRecSimple();


rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);

using (var parser = new ChoJSONWriter<EmployeeRecSimple>("emp.json"))


{
parser.Write(objs);
}
Above sample shows how to create JSON file from typed POCO class objects.

3.3. Configuration First Approach


In this model, we define the JSON configuration with all the necessary parameters
along with JSON columns required to generate the sample JSON file.

Listing 3.3.1 Define JSON configuration

Hide Copy Code


ChoJSONRecordConfiguration config = new ChoJSONRecordConfiguration();
config.JSONRecordFieldConfigurations.Add(new
ChoJSONRecordFieldConfiguration("Id"));
config.JSONRecordFieldConfigurations.Add(new
ChoJSONRecordFieldConfiguration("Name"));
In above, the class defines two JSON properties matching the sample JSON file
template.

Listing 3.3.2 Generate JSON file without POCO object

Hide Copy Code


List<ExpandoObject> objs = new List<ExpandoObject>();

dynamic rec1 = new ExpandoObject();


rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();


rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);

using (var parser = new ChoJSONWriter("emp.json", config))


{
parser.Write(objs);
}
The above sample code shows how to generate JSON file from list of dynamic objects
using predefined JSON configuration setup. In the JSONWriter constructor, we
specified the JSON configuration configuration object to obey the JSON layout
schema while creating the file. If there are any mismatch in the name or count of
JSON columns, will be reported as error and stops the writing process.

Listing 3.3.3 Saving JSON file with POCO object

Hide Copy Code


List<EmployeeRecSimple> objs = new List<EmployeeRecSimple>();

EmployeeRecSimple rec1 = new EmployeeRecSimple();


rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);

EmployeeRecSimple rec2 = new EmployeeRecSimple();


rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);

using (var parser = new ChoJSONWriter<EmployeeRecSimple>("emp.json", config))


{
parser.Write(objs);
}
Above sample code shows how to generate JSON file from list of POCO objects with
JSON configuration object. In the JSONWriter constructor, we specified the JSON
configuration configuration object.

3.4. Code First with declarative configuration

This is the combined approach to define POCO entity class along with attaching JSON
configuration parameters declaratively. id is required column and name is optional
value column with default value "XXXX". If name is not present, it will take the
default value.

Listing 3.4.1 Define POCO Object

Hide Copy Code


public class EmployeeRec
{
[ChoJSONRecordField]
[Required]
public int? Id
{
get;
set;
}

[ChoJSONRecordField]
[DefaultValue("XXXX")]
public string Name
{
get;
set;
}

public override string ToString()


{
return "{0}. {1}.".FormatString(Id, Name);
}
}
The code above illustrates about defining POCO object with nessasary attributes
required to generate JSON file. First thing defines property for each record field
with ChoJSONRecordFieldAttribute to qualify for JSON record mapping. Id is a
required property. We decorated it with RequiredAttribute. Name is given default
value using DefaultValueAttribute. It means that if the Name value is not set in
the object, JSONWriter spits the default value 'XXXX' to the file.

It is very simple and ready to save JSON data in no time.

Listing 3.4.2 Saving JSON file with POCO object

Hide Copy Code


List<EmployeeRec> objs = new List<EmployeeRec>();

EmployeeRec rec1 = new EmployeeRec();


rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);

EmployeeRec rec2 = new EmployeeRec();


rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);
using (var parser = new ChoJSONWriter<EmployeeRec>("emp.json"))
{
parser.Write(objs);
}
We start by creating a new instance of ChoJSONWriter object. That's all. All the
heavy lifting of genering JSON data from the objects is done by the writer under
the hood.

By default, JSONWriter discovers and uses default configuration parameters while


saving JSON file. These can be overridable according to your needs. The following
sections will give you in-depth details about each configuration attributes.

4. Writing All Records


It is as easy as setting up POCO object match up with JSON file structure,
construct the list of objects and pass it to JSONWriter's Write method. This will
write the entire list of objects into JSON file in one single call.

Listing 4.1 Write to JSON File

Hide Copy Code


List<EmployeeRec> objs = new List<EmployeeRec>();
//Construct and attach objects to this list
...

using (var parser = new ChoJSONWriter<EmployeeRec>("emp.json"))


{
parser.Write(objs);
}
or:

Listing 4.2 Writer to JSON file stream

Hide Copy Code


List<EmployeeRec> objs = new List<EmployeeRec>();
//Construct and attach objects to this list
...

using (var tx = File.OpenWrite("emp.json"))


{
using (var parser = new ChoJSONWriter<EmployeeRec>(tx))
{
parser.Write(objs);
}
}
This model keeps your code elegant, clean, easy to read and maintain.

5. Write Records Manually


This is an alternative way to write each and individual record to JSON file in case
when the POCO objects are constructed in a disconnected way.

Listing 5.1 Wrting to JSON file

Hide Copy Code


var writer = new ChoJSONWriter<EmployeeRec>("emp.json");

EmployeeRec rec1 = new EmployeeRec();


rec1.Id = 1;
rec1.Name = "Mark";
writer.Write(rec1);

EmployeeRec rec2 = new EmployeeRec();


rec1.Id = 2;
rec1.Name = "Jason";

writer.Write(rec2);
6. Customize JSON Record
Using ChoJSONRecordObjectAttribute, you can customize the POCO entity object
declaratively.

Listing 6.1 Customizing POCO object for each record

Hide Copy Code


[ChoJSONRecordObject]
public class EmployeeRec
{
[ChoJSONRecordField]
public int Id { get; set; }
[ChoJSONRecordField]
[Required]
[DefaultValue("ZZZ")]
public string Name { get; set; }
}
Here are the available attributes to carry out customization of JSON load operation
on a file.

Culture - The culture info used to read and write.


ColumnCountStrict - This flag indicates if an exception should be thrown if JSON
field configuration mismatch with the data object members.
NullValue - Special null value text expect to be treated as null value from JSON
file at the record level.
ErrorMode - This flag indicates if an exception should be thrown if writing and an
expected field is failed to write. This can be overridden per property. Possible
values are:
IgnoreAndContinue - Ignore the error, record will be skipped and continue with
next.
ReportAndContinue - Report the error to POCO entity if it is of
IChoNotifyRecordWrite type
ThrowAndStop - Throw the error and stop the execution
IgnoreFieldValueMode - N/A.
ObjectValidationMode - A flag to let the reader know about the type of validation
to be performed with record object. Possible values are:
Off - No object validation performed. (Default)
MemberLevel - Validation performed before each JSON property gets written to the
file.
ObjectLevel - Validation performed before all the POCO properties are written to
the file.
8. Customize JSON Fields
For each JSON column, you can specify the mapping in POCO entity property using
ChoJSONRecordFieldAttribute.

Listing 6.1 Customizing POCO object for JSON columns

Hide Copy Code


public class EmployeeRec
{
[ChoJSONRecordField]
public int Id { get; set; }
[ChoJSONRecordField]
[Required]
[DefaultValue("ZZZ")]
public string Name { get; set; }
}
Here are the available members to add some customization to it for each property:

FieldName - JSON field name. If not specified, POCO object property name will be
used as field name.
Size - Size of JSON column value.
NullValue - Special null value text expect to be treated as null value from JSON
file at the field level.
ErrorMode - This flag indicates if an exception should be thrown if writing and an
expected field failed to convert and write. Possible values are:
IgnoreAndContinue - Ignore the error and continue to load other properties of the
record.
ReportAndContinue - Report the error to POCO entity if it is of IChoRecord type.
ThrowAndStop - Throw the error and stop the execution.
8.1. DefaultValue
Any POCO entity property can be specified with default value using
System.ComponentModel.DefaultValueAttribute. It is the value used to write when the
JSON value null (controlled via IgnoreFieldValueMode).

8.2. ChoFallbackValue
Any POCO entity property can be specified with fallback value using
ChoETL.ChoFallbackValueAttribute. It is the value used when the property is failed
to writer to JSON. Fallback value only set when ErrorMode is either
IgnoreAndContinue or ReportAndContinue.

8.3. Type Converters


Most of the primitive types are automatically converted to string/text and save
them to JSON file. If the value of the JSON field aren't automatically be converted
into the text value, you can specify a custom / built-in .NET converters to convert
the value to text. These can be either IValueConverter, IChoValueConverteror
TypeConverter converters.

There are couple of ways you can specify the converters for each field

Declarative Approach
Configuration Approach
8.3.1. Declarative Approach
This model is applicable to POCO entity object only. If you have POCO class, you
can specify the converters to each property to carry out necessary conversion on
them. Samples below shows the way to do it.

Listing 8.3.1.1 Specifying type converters

Hide Copy Code


public class EmployeeRec
{
[ChoJSONRecordField]
[ChoTypeConverter(typeof(IntConverter))]
public int Id { get; set; }
[ChoJSONRecordField]
[Required]
[DefaultValue("ZZZ")]
public string Name { get; set; }
}
Listing 8.3.1.2 IntConverter implementation
Hide Copy Code
public class IntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
return value;
}

public object ConvertBack(object value, Type targetType, object parameter,


CultureInfo culture)
{
int intValue = (int)value;
return intValue.ToString("D4");
}
}
In the example above, we defined custom IntConverter class. And showed how to
format 'Id' JSON property with leading zeros.

8.3.2. Configuration Approach


This model is applicable to both dynamic and POCO entity object. This gives freedom
to attach the converters to each property at runtime. This takes the precedence
over the declarative converters on POCO classes.

Listing 8.3.2.1 Specifying TypeConverters

Hide Copy Code


ChoJSONRecordConfiguration config = new ChoJSONRecordConfiguration();

ChoJSONRecordFieldConfiguration idConfig = new


ChoJSONRecordFieldConfiguration("Id");
idConfig.AddConverter(new IntConverter());
config.JSONRecordFieldConfigurations.Add(idConfig);

config.JSONRecordFieldConfigurations.Add(new
ChoJSONRecordFieldConfiguration("Name"));
config.JSONRecordFieldConfigurations.Add(new
ChoJSONRecordFieldConfiguration("Name1"));
In above, we construct and attach the IntConverter to 'Id' field using AddConverter
helper method in ChoJSONRecordFieldConfiguration object.

Likewise, if you want to remove any converter from it, you can use RemoveConverter
on ChoJSONRecordFieldConfiguration object.

8.3.3. Custom Value Converter Approach


This approach allows to attach value converter to each JSON member using Fluenrt
API. This is quick way to handle any odd conversion process and avoid creating
value converter class.

Listing 8.3.3.1 POCO class

Hide Copy Code


public class EmployeeRec
{
[ChoJSONRecordField]
public int Id { get; set; }
[ChoJSONRecordField(2, FieldName ="Name", QuoteField = true)]
[Required]
[DefaultValue("ZZZ")]
public string Name { get; set; }
}
With the fluent API, sample below shows how to attach value converter to Id column

Listing 8.3.3.2 Attaching Value Converter

Hide Copy Code


using (var dr = new ChoJSONWriter<EmployeeRec>(@"Test.json")
.WithField(c => c.Id, valueConverter: (v) => ((int)value).ToString("C3",
CultureInfo.CurrentCulture))
)
{
Console.WriteLine(rec);
}
8.4. Validations
JSONWriter leverages both System.ComponentModel.DataAnnotations and Validation
Block validation attributes to specify validation rules for individual fields of
POCO entity. Refer to the MSDN site for a list of available DataAnnotations
validation attributes.

Listing 8.4.1 Using validation attributes in POCO entity

Hide Copy Code


[ChoJSONRecordObject]
public partial class EmployeeRec
{
[ChoJSONRecordField(1, FieldName = "id")]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, int.MaxValue, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }

[ChoJSONRecordField]
[Required]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }
}
In example above, used Range validation attribute for Id property. Required
validation attribute to Name property. JSONWriter performs validation on them
before saving the data to file when Configuration.ObjectValidationMode is set to
ChoObjectValidationMode.MemberLevel or ChoObjectValidationMode.ObjectLevel.

Some cases, you may want to take control and perform manual self validation within
the POCO entity class. This can be achieved by inheriting POCO object from
IChoValidatable interface.

Listing 8.4.2 Manual validation on POCO entity

Hide Copy Code


[ChoJSONRecordObject]
public partial class EmployeeRec : IChoValidatable
{
[ChoJSONRecordField]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, int.MaxValue, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }
[ChoJSONRecordField]
[Required]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }

public bool TryValidate(object target, ICollection<ValidationResult>


validationResults)
{
return true;
}

public bool TryValidateFor(object target, string memberName,


ICollection<ValidationResult> validationResults)
{
return true;
}
}
Sample above shows how to implement custom self-validation in POCO object.

IChoValidatable interface exposes below methods

TryValidate - Validate entire object, return true if all validation passed.


Otherwise return false.
TryValidateFor - Validate specific property of the object, return true if all
validation passed. Otherwise return false.
8.5. ChoIgnoreMember
If you want to ignore a POCO class member from JSON parsing in OptOut mode,
decorate them with ChoIgnoreMemberAttribute. Sample below shows Title member is
ignored from JSON loading process.

Listing 8.5.1 Ignore a member

Hide Copy Code

Hide Copy Code


public class EmployeeRec
{
public int Id { get; set; }
public string Name { get; set; }
[ChoIgnoreMember]
public string Title { get; set; }
}
8.6. StringLength
In OptOut mode, you can specify the size of the JSON column by using
System.ComponentModel.DataAnnotations.StringLengthAttribute.

Listing 8.6.1 Specifying Size of JSON member

Hide Copy Code

Hide Copy Code


public class EmployeeRec
{
public int Id { get; set; }
[StringLength(25)]
public string Name { get; set; }
[ChoIgnoreMember]
public string Title { get; set; }
}
8.6. Display
In OptOut mode, you can specify the name of JSON column mapped to member using
System.ComponentModel.DataAnnotations.DisplayAttribute.

Listing 8.6.1 Specifying name of JSON column

Hide Copy Code

Hide Copy Code


public class EmployeeRec
{
public int Id { get; set; }
[Display(Name="FullName")]
[StringLength(25)]
public string Name { get; set; }
[ChoIgnoreMember]
public string Title { get; set; }
}
8.7. DisplayName
In OptOut mode, you can specify the name of JSON column mapped to member using
System.ComponentModel.DataAnnotations.DisplayNameAttribute.

Listing 8.7.1 Specifying name of JSON column

Hide Copy Code


public class EmployeeRec
{
public int Id { get; set; }
[Display(Name="FullName")]
[StringLength(25)]
public string Name { get; set; }
[ChoIgnoreMember]
public string Title { get; set; }
}
10. Callback Mechanism
JSONWriter offers industry standard JSON data file generation out of the box to
handle most of the needs. If the generation process is not handling any of your
needs, you can use the callback mechanism offered by JSONWriter to handle such
situations. In order to participate in the callback mechanism, you can use either
of the following models

Using event handlers exposed by JSONWriter via IChoWriter interface.


Inheriting POCO entity object from IChoNotifyRecordWrite / IChoNotifyFileWrite /
IChoNotifyRecordFieldWrite interfaces
Inheriting DataAnnotation's MetadataType type object by IChoNotifyRecordWrite /
IChoNotifyFileWrite / IChoNotifyRecordFieldWrite interfaces.
In order to participate in the callback mechanism, Either POCO entity object or
DataAnnotation's MetadataType type object must be inherited by
IChoNotifyRecordWrite interface.

Tip: Any exceptions raised out of these interface methods will be ignored.

IChoRecorder exposes the below methods:

BeginWrite - Invoked at the begin of the JSON file write


EndWrite - Invoked at the end of the JSON file write
BeforeRecordWrite - Raised before the JSON record write
AfterRecordWrite - Raised after JSON record write
RecordWriteError - Raised when JSON record errors out while writing
BeforeRecordFieldWrite - Raised before JSON column value write
AfterRecordFieldWrite - Raised after JSON column value write
RecordFieldWriteError - Raised when JSON column value errors out while writing
IChoNotifyRecordWrite exposes the below methods:

BeforeRecordWrite - Raised before the JSON record write


AfterRecordWrite - Raised after JSON record write
RecordWriteError - Raised when JSON record write errors out
IChoNotifyFileWrite exposes the below methods:

BeginWrite - Invoked at the begin of the JSON file write


EndWrite - Invoked at the end of the JSON file write
IChoNotifyRecordFieldWrite exposes the below methods:

BeforeRecordFieldWrite - Raised before JSON column value write


AfterRecordFieldWrite - Raised after JSON column value write
RecordFieldWriteError - Raised when JSON column value write errors out
IChoNotifyFileHeaderArrange exposes the below methods:

FileHeaderArrange - Raised before JSON file header is written to file, an


opportunity to rearrange the JSON columns
IChoNotifyFileHeaderWrite exposes the below methods:

FileHeaderWrite - Raised before JSON file header is written to file, an opportunity


to customize the header.
10.1 Using JSONWriter events
This is more direct and simplest way to subscribe to the callback events and handle
your odd situations in parsing JSON files. Downside is that code can't be reusable
as you do by implementing IChoNotifyRecordRead with POCO record object.

Sample below shows how to use the BeforeRecordLoad callback method to skip lines
stating with '%' characters.

Listing 10.1.1 Using JSONWriter callback events

Hide Copy Code


static void IgnoreLineTest()
{
using (var parser = new ChoJSONWriter("emp.json"))
{
parser.BeforeRecordWrite += (o, e) =>
{
if (e.Source != null)
{
e.Skip = ((JObject)e.Source).Contains("name1");
}
};

parser.Write(rec);
}
}
Likewise you can use other callback methods as well with JSONWriter.

10.2 Implementing IChoNotifyRecordWrite interface


Sample below shows how to implement IChoNotifyRecordWrite interface to direct POCO
class.
Listing 10.2.1 Direct POCO callback mechanism implementation

Hide Shrink Copy Code


[ChoJSONRecordObject]
public partial class EmployeeRec : IChoNotifyrRecordWrite
{
[ChoJSONRecordField]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, int.MaxValue, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }

[ChoJSONRecordField]
[Required]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }

public bool AfterRecordWrite(object target, int index, object source)


{
throw new NotImplementedException();
}

public bool BeforeRecordWrite(object target, int index, ref object source)


{
throw new NotImplementedException();
}

public bool RecordWriteError(object target, int index, object source, Exception


ex)
{
throw new NotImplementedException();
}
}
Sample below shows how to attach Metadata class to POCO class by using
MetadataTypeAttribute on it.

Listing 10.2.2 MetaDataType based callback mechanism implementation

Hide Shrink Copy Code


[ChoJSONRecordObject]
public class EmployeeRecMeta : IChoNotifyRecordWrite
{
[ChoJSONRecordField]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, int.MaxValue, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }

[ChoJSONRecordField]
[Required]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }

public bool AfterRecordWrite(object target, int index, object source)


{
throw new NotImplementedException();
}
public bool BeforeRecordWrite(object target, int index, ref object source)
{
throw new NotImplementedException();
}

public bool RecordWriteError(object target, int index, object source, Exception


ex)
{
throw new NotImplementedException();
}
}

[MetadataType(typeof(EmployeeRecMeta))]
public partial class EmployeeRec
{
public int Id { get; set; }
public string Name { get; set; }
}
Sample below shows how to attach Metadata class for sealed or third party POCO
class by using ChoMetadataRefTypeAttribute on it.

Listing 10.2.2 MetaDataType based callback mechanism implementation

Hide Shrink Copy Code


[ChoMetadataRefType(typeof(EmployeeRec))]
[ChoJSONRecordObject]
public class EmployeeRecMeta : IChoNotifyRecordWrite
{
[ChoJSONRecordField]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, int.MaxValue, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }

[ChoJSONRecordField]
[Required]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }

public bool AfterRecordWrite(object target, int index, object source)


{
throw new NotImplementedException();
}

public bool BeforeRecordWrite(object target, int index, ref object source)


{
throw new NotImplementedException();
}

public bool RecordWriteError(object target, int index, object source, Exception


ex)
{
throw new NotImplementedException();
}
}

public partial class EmployeeRec


{
public int Id { get; set; }
public string Name { get; set; }
}
10.1 BeginWrite
This callback invoked once at the beginning of the JSON file write. source is the
JSON file stream object. In here you have chance to inspect the stream, return true
to continue the JSON generation. Return false to stop the generation.

Listing 10.1.1 BeginWrite Callback Sample

Hide Copy Code


public bool BeginWrite(object source)
{
StreamReader sr = source as StreamReader;
return true;
}
10.2 EndWrite
This callback invoked once at the end of the JSON file generation. source is the
JSON file stream object. In here you have chance to inspect the stream, do any post
steps to be performed on the stream.

Listing 10.2.1 EndWrite Callback Sample

Hide Copy Code


public void EndWrite(object source)
{
StreamReader sr = source as StreamReader;
}
10.3 BeforeRecordWrite
This callback invoked before each POCO record object is written to JSON file.
target is the instance of the POCO record object. index is the line index in the
file. source is the JSON record line. In here you have chance to inspect the POCO
object, and generate the JSON record line if needed.

Tip: If you want to skip the record from writing, set the source to null.

Return true to continue the load process, otherwise return false to stop the
process.

Listing 10.3.1 BeforeRecordWrite Callback Sample

Hide Copy Code


public bool BeforeRecordWrite(object target, int index, ref object source)
{
return true;
}
10.4 AfterRecordWrite
This callback invoked after each POCO record object is written to JSON file. target
is the instance of the POCO record object. index is the line index in the file.
source is the JSON record line. In here you have chance to do any post step
operation with the record line.

Return true to continue the load process, otherwise return false to stop the
process.

Listing 10.4.1 AfterRecordWrite Callback Sample

Hide Copy Code


public bool AfterRecordWrite(object target, int index, object source)
{
return true;
}
10.5 RecordWriteError
This callback invoked if error encountered while writing POCO record object. target
is the instance of the POCO record object. index is the line index in the file.
source is the JSON record line. ex is the exception object. In here you have chance
to handle the exception. This method invoked only when Configuration.ErrorMode is
ReportAndContinue.

Return true to continue the load process, otherwise return false to stop the
process.

Listing 10.5.1 RecordWriteError Callback Sample

Hide Copy Code


public bool RecordLoadError(object target, int index, object source, Exception ex)
{
return true;
}
10.6 BeforeRecordFieldWrite
This callback invoked before each JSON record column is written to JSON file.
target is the instance of the POCO record object. index is the line index in the
file. propName is the JSON record property name. value is the JSON column value. In
here, you have chance to inspect the JSON record property value and perform any
custom validations etc.

Return true to continue the load process, otherwise return false to stop the
process.

Listing 10.6.1 BeforeRecordFieldWrite Callback Sample

Hide Copy Code


public bool BeforeRecordFieldWrite(object target, int index, string propName, ref
object value)
{
return true;
}
10.7 AfterRecordFieldWrite
This callback invoked after each JSON record column value is written to JSON file.
target is the instance of the POCO record object. index is the line index in the
file. propName is the JSON record property name. value is the JSON column value.
Any post field operation can be performed here, like computing other properties,
validations etc.

Return true to continue the load process, otherwise return false to stop the
process.

Listing 10.7.1 AfterRecordFieldWrite Callback Sample

Hide Copy Code


public bool AfterRecordFieldWrite(object target, int index, string propName, object
value)
{
return true;
}
10.8 RecordWriteFieldError
This callback invoked when error encountered while writing JSON record column
value. target is the instance of the POCO record object. index is the line index in
the file. propName is the JSON record property name. value is the JSON column
value. ex is the exception object. In here you have chance to handle the exception.
This method invoked only after the below two sequences of steps performed by the
JSONWriter

JSONWriter looks for FallbackValue value of each JSON property. If present, it


tries to use it to write.
If the FallbackValue value not present and the Configuration.ErrorMode is specified
as ReportAndContinue., this callback will be executed.
Return true to continue the load process, otherwise return false to stop the
process.

Listing 10.8.1 RecordFieldWriteError Callback Sample

Hide Copy Code


public bool RecordFieldWriteError(object target, int index, string propName, object
value, Exception ex)
{
return true;
}
11. Customization
JSONWriter automatically detects and loads the configuration settings from POCO
entity. At runtime, you can customize and tweak these parameters before JSON
generation. JSONWriter exposes Configuration property, it is of
ChoJSONRecordConfiguration object. Using this property, you can perform the
customization.

Listing 11.1 Customizing JSONWriter at run-time

Hide Copy Code


class Program
{
static void Main(string[] args)
{
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();


rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);

using (var parser = new ChoJSONWriter("emp.json"))


{
parser.Configuration.ColumnCountStrict = true;
parser.Write(objs);
}
}
}
12. Using Dynamic Object
So far, the article explained about using JSONWriter with POCO object. JSONWriter
also supports generating JSON file without POCO entity objects It leverages .NET
dynamic feature. The sample below shows how to generate JSON stream using dynamic
objects. The JSON schema is determined from first object. If there is mismatch
found in the dynamic objects member values, error will be raised and stop the
generation process.

The sample below shows it:

Listing 12.1 Generating JSON file from dynamic objects

Hide Copy Code


class Program
{
static void Main(string[] args)
{
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();


rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);

using (var parser = new ChoJSONWriter("emp.json"))


{
parser.Configuration.ColumnCountStrict = true;
parser.Write(objs);
}
}
}
13. Exceptions
JSONWriter throws different types of exceptions in different situations.

ChoParserException - JSON file is bad and parser not able to recover.


ChoRecordConfigurationException - Any invalid configuration settings are specified,
this exception will be raised.
ChoMissingRecordFieldException - A property is missing for a JSON column, this
exception will be raised.
15. Using MetadataType Annotation
Cinchoo ETL works better with data annotation's MetadataType model. It is way to
attach MetaData class to data model class. In this associated class, you provide
additional metadata information that is not in the data model. It roles is to add
attribute to a class without having to modify this one. You can add this attribute
that takes a single parameter to a class that will have all the attributes. This is
useful when the POCO classes are auto generated (by Entity Framework, MVC etc) by
an automatic tools. This is why second class come into play. You can add new stuffs
without touching the generated file. Also this promotes modularization by
separating the concerns into multiple classes.

For more information about it, please search in MSDN.

Listing 15.1 MetadataType annotation usage sample

Hide Shrink Copy Code


[MetadataType(typeof(EmployeeRecMeta))]
public class EmployeeRec
{
public int Id { get; set; }
public string Name { get; set; }
}
[ChoJSONRecordObject]
public class EmployeeRecMeta : IChoNotifyRecordWrite, IChoValidatable
{
[ChoJSONRecordField]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, 1, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }

[ChoJSONRecordField]
[StringLength(1)]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }

public bool AfterRecordWrite(object target, int index, object source)


{
throw new NotImplementedException();
}

public bool BeforeRecordWrite(object target, int index, ref object source)


{
throw new NotImplementedException();
}

public bool RecordWriteError(object target, int index, object source, Exception


ex)
{
throw new NotImplementedException();
}

public bool TryValidate(object target, ICollection<ValidationResult>


validationResults)
{
return true;
}

public bool TryValidateFor(object target, string memberName,


ICollection<ValidationResult> validationResults)
{
return true;
}
}
In above EmployeeRec is the data class. Contains only domain specific properties
and operations. Mark it very simple class to look at it.

We separate the validation, callback mechanism, configuration etc into metadata


type class, EmployeeRecMeta.

16. Configuration Choices


If the POCO entity class is an auto-generated class or exposed via library or it is
a sealed class, it limits you to attach JSON schema definition to it declaratively.
In such case, you can choose one of the options below to specify JSON layout
configuration

Manual Configuration
Auto Map Configuration
Attaching MetadataType class
I'm going to show you how to configure the below POCO entity class on each approach

Listing 16.1 Sealed POCO entity class

Hide Copy Code


public sealed class EmployeeRec
{
public int Id { get; set; }
public string Name { get; set; }
}
16.1 Manual Configuration
Define a brand new configuration object from scratch and add all the necessary JSON
fields to the ChoJSONConfiguration.JSONRecordFieldConfigurations collection
property. This option gives you greater flexibility to control the configuration of
JSON parsing. But the downside is that possibility of making mistakes and hard to
manage them if the JSON file layout is large,

Listing 16.1.1 Manual Configuration

Hide Copy Code


ChoJSONRecordConfiguration config = new ChoJSONRecordConfiguration();
config.ThrowAndStopOnMissingField = true;
config.JSONRecordFieldConfigurations.Add(new
ChoJSONRecordFieldConfiguration("Id"));
config.JSONRecordFieldConfigurations.Add(new
ChoJSONRecordFieldConfiguration("Name"));
16.2 Auto Map Configuration
This is an alternative approach and very less error-prone method to auto map the
JSON columns for the POCO entity class.

First define a schema class for EmployeeRec POCO entity class as below

Listing 16.2.1 Auto Map class

Hide Copy Code


public class EmployeeRecMap
{
[ChoJSONRecordField]
public int Id { get; set; }

[ChoJSONRecordField]
public string Name { get; set; }
}
Then you can use it to auto map JSON columns by using
ChoJSONRecordConfiguration.MapRecordFields method

Listing 16.2.2 Using Auto Map configuration

Hide Copy Code


ChoJSONRecordConfiguration config = new ChoJSONRecordConfiguration();
config.MapRecordFields<EmployeeRecMap>();

EmployeeRec rec1 = new EmployeeRec();


rec1.Id = 2;
rec1.Name = "Jason";

foreach (var e in new ChoJSONWriter<EmployeeRec>("emp.json", config))


w.Write(rec1);
16.3 Attaching MetadataType class
This is one another approach to attach MetadataType class for POCO entity object.
Previous approach simple care for auto mapping of JSON columns only. Other
configuration properties like property converters, parser parameters,
default/fallback values etc. are not considered.

This model, accounts for everything by defining MetadataType class and specifying
the JSON configuration parameters declaratively. This is useful when your POCO
entity is sealed and not partial class. Also it is one of favorable and less error-
prone approach to configure JSON parsing of POCO entity.

Listing 16.3.1 Define MetadataType class

Hide Shrink Copy Code


[ChoJSONRecordObject]
public class EmployeeRecMeta : IChoNotifyRecordWrite, IChoValidatable
{
[ChoJSONRecordField]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, 1, ErrorMessage = "Id must be > 0.")]
public int Id { get; set; }

[ChoJSONRecordField]
[StringLength(1)]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }

public bool AfterRecordWrite(object target, int index, object source)


{
throw new NotImplementedException();
}

public bool BeforeRecordWrite(object target, int index, ref object source)


{
throw new NotImplementedException();
}

public bool RecordWriteError(object target, int index, object source, Exception


ex)
{
throw new NotImplementedException();
}

public bool TryValidate(object target, ICollection<ValidationResult>


validationResults)
{
return true;
}

public bool TryValidateFor(object target, string memberName,


ICollection<ValidationResult> validationResults)
{
return true;
}
}
Listing 16.3.2 Attaching MetadataType class

Hide Copy Code


//Attach metadata
ChoMetadataObjectCache.Default.Attach<EmployeeRec>(new EmployeeRecMeta());

using (var tx = File.OpenWrite("emp.json"))


{
using (var parser = new ChoJSONWriter<EmployeeRec>(tx))
{
parser.Write(objs);
}
}
17. ToTextAll Helper Method
This is little nifty helper method to generate JSON formatted output from list of
objects. It helps you to run and play with different options to see the JSON output
quickly in test environment.

Hide Copy Code


static void ToTextTest()
{
List<EmployeeRec> objs = new List<EmployeeRec>();
EmployeeRec rec1 = new EmployeeRec();
rec1.Id = 10;
rec1.Name = "Mark";
objs.Add(rec1);

EmployeeRec rec2 = new EmployeeRec();


rec2.Id = 200;
rec2.Name = "Lou";
objs.Add(rec2);

Console.WriteLine(ChoJSONWriter.ToTextAll(objs));
}
17a. ToText Helper Method
This is little nifty helper method to generate JSON formatted output from an
object. It helps you to run and play with different options to see the JSON output
quickly in test environment.

Hide Copy Code


static void ToTextTest()
{
EmployeeRec rec1 = new EmployeeRec();
rec1.Id = 10;
rec1.Name = "Mark";
objs.Add(rec1);

Console.WriteLine(ChoJSONWriter.ToText(rec1));
}
18. Writing DataReader Helper Method
This helper method lets you to create JSON file / stream from ADO.NET DataReader.

Hide Copy Code


static void WriteDataReaderTest()
{
SqlConnection conn = new SqlConnection(connString);
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM Members", conn);
IDataReader dr = cmd.ExecuteReader();

StringBuilder json = new StringBuilder();


using (var parser = new ChoJSONWriter(json))
{
parser.Write(dr);
}
Console.WriteLine(json.ToString());
}
19. Writing DataTable Helper Method
This helper method lets you to create JSON file / stream from ADO.NET DataTable.

Hide Copy Code


static void WriteDataTableTest()
{
string connString = @"Data Source=(localdb)\v11.0;Initial
Catalog=TestDb;Integrated Security=True";

SqlConnection conn = new SqlConnection(connString);


conn.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM Members", conn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);

StringBuilder json = new StringBuilder();


using (var parser = new ChoJSONWriter(json)
)
{
parser.Write(dt);
}

Console.WriteLine(json.ToString());
}
20. Advanced Topics
20.1 Override Converters Format Specs
Cinchoo ETL automatically parses and converts each JSON column values to the
corresponding JSON column's underlying data type seamlessly. Most of the basic .NET
types are handled automatically without any setup needed.

This is achieved through two key settings in the ETL system

ChoJSONRecordConfiguration.CultureInfo - Represents information about a specific


culture including the names of the culture, the writing system, and the calendar
used, as well as access to culture-specific objects that provide information for
common operations, such as formatting dates and sorting strings. Default is 'en-
US'.
ChoTypeConverterFormatSpec - It is global format specifier class holds all the
intrinsic .NET types formatting specs.
In this section, I'm going to talk about changing the default format specs for each
.NET intrinsic data types according to parsing needs.

ChoTypeConverterFormatSpec is singleton class, the instance is exposed via


'Instance' static member. It is thread local, means that there will be separate
instance copy kept on each thread.

There are 2 sets of format specs members given to each intrinsic type, one for
loading and another one for writing the value, except for Boolean, Enum, DataTime
types. These types have only one member for both loading and writing operations.

Specifying each intrinsic data type format specs through ChoTypeConverterFormatSpec


will impact system wide. ie. By setting ChoTypeConverterFormatSpec.IntNumberStyle =
NumberStyles.AllowParentheses, will impact all integer members of JSON objects to
allow parentheses. If you want to override this behavior and take control of
specific JSON data member to handle its own unique parsing of JSON value from
global system wide setting, it can be done by specifying TypeConverter at the JSON
field member level. Refer section 13.4 for more information.

Listing 20.1.1 ChoTypeConverterFormatSpec Members

Hide Shrink Copy Code


public class ChoTypeConverterFormatSpec
{
public static readonly ThreadLocal<ChoTypeConverterFormatSpec> Instance = new
ThreadLocal<ChoTypeConverterFormatSpec>(() => new ChoTypeConverterFormatSpec());

public string DateTimeFormat { get; set; }


public ChoBooleanFormatSpec BooleanFormat { get; set; }
public ChoEnumFormatSpec EnumFormat { get; set; }

public NumberStyles? CurrencyNumberStyle { get; set; }


public string CurrencyFormat { get; set; }

public NumberStyles? BigIntegerNumberStyle { get; set; }


public string BigIntegerFormat { get; set; }

public NumberStyles? ByteNumberStyle { get; set; }


public string ByteFormat { get; set; }

public NumberStyles? SByteNumberStyle { get; set; }


public string SByteFormat { get; set; }

public NumberStyles? DecimalNumberStyle { get; set; }


public string DecimalFormat { get; set; }

public NumberStyles? DoubleNumberStyle { get; set; }


public string DoubleFormat { get; set; }

public NumberStyles? FloatNumberStyle { get; set; }


public string FloatFormat { get; set; }

public string IntFormat { get; set; }


public NumberStyles? IntNumberStyle { get; set; }

public string UIntFormat { get; set; }


public NumberStyles? UIntNumberStyle { get; set; }

public NumberStyles? LongNumberStyle { get; set; }


public string LongFormat { get; set; }

public NumberStyles? ULongNumberStyle { get; set; }


public string ULongFormat { get; set; }

public NumberStyles? ShortNumberStyle { get; set; }


public string ShortFormat { get; set; }

public NumberStyles? UShortNumberStyle { get; set; }


public string UShortFormat { get; set; }
}
Sample below shows how to load JSON data stream having 'se-SE' (Swedish) culture
specific data using JSONWriter. Also the input feed comes with 'EmployeeNo' values
containing parentheses. In order to make the load successful, we have to set the
ChoTypeConverterFormatSpec.IntNumberStyle to NumberStyles.AllowParenthesis.
Listing 20.1.2 Using ChoTypeConverterFormatSpec in code

Hide Shrink Copy Code


static void FormatSpecDynamicTest()
{
ChoTypeConverterFormatSpec.Instance.DateTimeFormat = "d";
ChoTypeConverterFormatSpec.Instance.BooleanFormat = ChoBooleanFormatSpec.YOrN;

List<ExpandoObject> objs = new List<ExpandoObject>();


dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();


rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
objs.Add(rec2);

StringBuilder json = new StringBuilder();


using (var parser = new ChoJSONWriter(json)
)
{
parser.Write(objs);
}

Console.WriteLine(json.ToString());
}
20.2 Currency Support
Cinchoo ETL provides ChoCurrency object to read and write currency values in JSON
files. ChoCurrency is a wrapper class to hold the currency value in decimal type
along with support of serializing them in text format during JSON load.

Listing 20.2.1 Using Currency members in dynamic model

Hide Shrink Copy Code


static void CurrencyDynamicTest()
{
ChoTypeConverterFormatSpec.Instance.CurrencyFormat = "C2";

List<ExpandoObject> objs = new List<ExpandoObject>();


dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();


rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
objs.Add(rec2);

StringBuilder json = new StringBuilder();


using (var parser = new ChoJSONWriter(json)
)
{
parser.Write(objs);
}

Console.WriteLine(json.ToString());
}
Sample above shows how to output currency values using dynamic object model. As the
currency output will have thousand comma separator, this will fail to generate JSON
file. To overcome this issue, we specify the writer to quote all fields.

PS: The format of the currency value is figured by JSONWriter through


ChoRecordConfiguration.Culture and ChoTypeConverterFormatSpec.CurrencyFormat.

Sample below shows how to use ChoCurrency JSON field in POCO entity class.

Listing 20.2.2 Using Currency members in POCO model

Hide Shrink Copy Code


public class EmployeeRecWithCurrency
{
public int Id { get; set; }
public string Name { get; set; }
public ChoCurrency Salary { get; set; }
}

static void CurrencyPOCOTest()


{
List<EmployeeRecWithCurrency> objs = new List<EmployeeRecWithCurrency>();
EmployeeRecWithCurrency rec1 = new EmployeeRecWithCurrency();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.Salary = new ChoCurrency(100000);
objs.Add(rec1);

EmployeeRecWithCurrency rec2 = new EmployeeRecWithCurrency();


rec2.Id = 200;
rec2.Name = "Lou";
rec2.Salary = new ChoCurrency(150000);
objs.Add(rec2);

StringBuilder json = new StringBuilder();


using (var parser = new ChoJSONWriter<EmployeeRecWithCurrency>(json)
)
{
parser.Write(objs);
}

Console.WriteLine(json.ToString());
}
20.3 Enum Support
Cinchoo ETL implicitly handles parsing/writing of enum column values from JSON
files. If you want to fine control the parsing of these values, you can specify
them globally via ChoTypeConverterFormatSpec.EnumFormat. Default is
ChoEnumFormatSpec.Value

FYI, changing this value will impact system wide.

There are 3 possible values can be used

ChoEnumFormatSpec.Value - Enum value is used for parsing.


ChoEnumFormatSpec.Name - Enum key name is used for parsing.
ChoEnumFormatSpec.Description - If each enum key is decorated with
DescriptionAttribute, its value will be use for parsing.
Listing 20.3.1 Specifying Enum format specs during parsing

Hide Shrink Copy Code


public enum EmployeeType
{
[Description("Full Time Employee")]
Permanent = 0,
[Description("Temporary Employee")]
Temporary = 1,
[Description("Contract Employee")]
Contract = 2
}

static void EnumTest()


{
ChoTypeConverterFormatSpec.Instance.EnumFormat = ChoEnumFormatSpec.Description;

List<ExpandoObject> objs = new List<ExpandoObject>();


dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
rec1.Status = EmployeeType.Permanent;
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();


rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
rec2.Status = EmployeeType.Contract;
objs.Add(rec2);

StringBuilder json = new StringBuilder();


using (var parser = new ChoJSONWriter(json)
)
{
parser.Write(objs);
}

Console.WriteLine(json.ToString());
}
20.4 Boolean Support
Cinchoo ETL implicitly handles parsing/writing of boolean JSON column values from
JSON files. If you want to fine control the parsing of these values, you can
specify them globally via ChoTypeConverterFormatSpec.BooleanFormat. Default value
is ChoBooleanFormatSpec.ZeroOrOne

FYI, changing this value will impact system wide.

There are 4 possible values can be used

ChoBooleanFormatSpec.ZeroOrOne - '0' for false. '1' for true.


ChoBooleanFormatSpec.YOrN - 'Y' for true, 'N' for false.
ChoBooleanFormatSpec.TrueOrFalse - 'True' for true, 'False' for false.
ChoBooleanFormatSpec.YesOrNo - 'Yes' for true, 'No' for false.
Listing 20.4.1 Specifying boolean format specs during parsing

Hide Shrink Copy Code


static void BoolTest()
{
ChoTypeConverterFormatSpec.Instance.BooleanFormat = ChoBooleanFormatSpec.YOrN;

List<ExpandoObject> objs = new List<ExpandoObject>();


dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
rec1.Status = EmployeeType.Permanent;
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();


rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
rec2.Status = EmployeeType.Contract;
objs.Add(rec2);

StringBuilder json = new StringBuilder();


using (var parser = new ChoJSONWriter(json)
)
{
parser.Write(objs);
}

Console.WriteLine(json.ToString());
}
20.5 DateTime Support
Cinchoo ETL implicitly handles parsing/writing of datetime JSON column values from
JSON files using system Culture or custom set culture. If you want to fine control
the parsing of these values, you can specify them globally via
ChoTypeConverterFormatSpec.DateTimeFormat. Default value is 'd'.

FYI, changing this value will impact system wide.

You can use any valid standard or custom datetime .NET format specification to
parse the datetime JSON values from the file.

Listing 20.5.1 Specifying datetime format specs during parsing

Hide Shrink Copy Code


static void DateTimeDynamicTest()
{
ChoTypeConverterFormatSpec.Instance.DateTimeFormat = "MMM dd, yyyy";

List<ExpandoObject> objs = new List<ExpandoObject>();


dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();


rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
objs.Add(rec2);

StringBuilder json = new StringBuilder();


using (var parser = new ChoJSONWriter(json)
)
{
parser.Write(objs);
}

Console.WriteLine(json.ToString());
}
Sample above shows how to generate custom datetime values to JSON file.

Note: As the datetime values contains JSON separator, we instruct the writer to
quote all fields.

21. Fluent API


JSONWriter exposes few frequent to use configuration parameters via fluent API
methods. This will make the programming of generating JSON files quicker.

21.1. NullValueHandling
Specifies null value handling options for the ChoJSONWriter

Ignore - Ignore null values while writing JSON

Default - Include null values while writing JSON

21.2. Formatting
Specifies formatting options for the ChoJSONWriter

None - No special formatting is applied. This is the default.

Intented- Causes child objects to be indented.

21.3 WithFields
This API method specifies the list of JSON fields to be considered for writing JSON
file. Other fields will be discarded. Field names are case-insensitive.

Hide Shrink Copy Code


static void QuickDynamicTest()
{
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();


rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
objs.Add(rec2);

StringBuilder json = new StringBuilder();


using (var parser = new ChoJSONWriter(json)
.WithFields("Id", "Name")
)
{
parser.Write(objs);
}

Console.WriteLine(json.ToString());
}
21.4 WithField
This API method used to add JSON column with specific date type, quote flag, and/or
quote character. This method helpful in dynamic object model, by specifying each
and individual JSON column with appropriate datatype.

Hide Shrink Copy Code


static void QuickDynamicTest()
{
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();


rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
objs.Add(rec2);

StringBuilder json = new StringBuilder();


using (var parser = new ChoJSONWriter(json)
.WithField("Id", typeof(int))
.WithField("Name"))
)
{
parser.Write(objs);
}

Console.WriteLine(json.ToString());
}
21.5. IgnoreFieldValueMode
Specifies ignore field value for the ChoJSONWriter

None - Ignore field value is turned off. This is the default.

DbNull- DBNull value will be ignored.

Empty - Empty text value will be ignored.

WhiteSpace - Whitespace text will be ignored.

21.6 ColumnCountStrict
This API method used to set the JSONWriter to perform check on column countnness
before writing JSON file.

Hide Shrink Copy Code


static void ColumnCountTest()
{
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();


rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
objs.Add(rec2);

StringBuilder json = new StringBuilder();


using (var parser = new ChoJSONWriter(json)
.ColumnCountStrict()
)
{
parser.Write(objs);
}

Console.WriteLine(json.ToString());
}
21.7. Configure
This API method used to configure all configuration parameters which are not
exposed via fluent API.

Hide Copy Code


static void ConfigureTest()
{
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();


rec2.Id = 200;
rec2.Name = "Lou";
objs.Add(rec2);

StringBuilder json = new StringBuilder();


using (var parser = new ChoJSONWriter(json)
.Configure(c => c.ErrorMode = ChoErrorMode.ThrowAndStop)
)
{
parser.Write(objs);
}

Console.WriteLine(json.ToString());
}
21.8. Setup
This API method used to setup the writer's parameters / events via fluent API.

Hide Copy Code


static void SetupTest()
{
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();


rec2.Id = 200;
rec2.Name = "Lou";
objs.Add(rec2);

StringBuilder json = new StringBuilder();


using (var parser = new ChoJSONWriter(json)
.Setup(r => r.BeforeRecordWrite += (o, e) =>
{
})
)
{
parser.Write(objs);
}

Console.WriteLine(json.ToString());
}
License
This article, along with any associated source code and files, is licensed under
The Code Project Open License (CPOL)

Share
About the Author

Cinchoo
United States United States
No Biography provided
Comments and Discussions

You must Sign In to use this message board.


Search Comments
Submit
Spacing
Relaxed
Layout
Normal
Per page
25

First Prev Next


General
My vote of 5 Pin Member Olfello 10-Jun-20 8:29
Last Visit: 31-Dec-99 18:00 Last Update: 12-Jun-20 4:14Refresh 1
General General News News Suggestion Suggestion Question Question Bug
Bug Answer Answer Joke Joke Praise Praise Rant Rant Admin Admin

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads,


Ctrl+Shift+Left/Right to switch pages.

Article
View Stats
Revisions (2)
Comments (1)
Posted 8 Jun 2020
Tagged as
C#
.NET
VB.NET
Stats
1.7K views
1 bookmarked
Go to top
Permalink
Advertise
Privacy
Cookies
Terms of Use
Layout: fixed | fluid

Article Copyright 2020 by Cinchoo


Everything else Copyright © CodeProject, 1999-2020

Web01 2.8.200606.1

You might also like