Introduction
POCO2TS
is a command line program that generates TypeScript interfaces from POCO classes of assemblies built on .NET Framework or .NET Core. It does not matter if your .NET codes are in C# or VB.
Background
This is a by-product of WebApiClientGen. If you are developing Web client programs using jQuery or Angular 2+, you may want more than what POCO2TS
offers, which is actually integrated in WebApiClientGen
. If so, you may skip this article and instead, read the following:
Using the Code
You have POCO classes in assembly DemoWebApi.DemoData.dll, which are decorated by DataContractAttribute
, to be used for data models, WCF, Entity Framework Code First and serialization with XML or JSON. Check this example in C#.
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Collections.ObjectModel;
namespace DemoWebApi.DemoData
{
public sealed class Constants
{
public const string DataNamespace = "http://fonlow.com/DemoData/2014/02";
}
[DataContract(Namespace = Constants.DataNamespace)]
public enum AddressType
{
[EnumMember]
Postal,
[EnumMember]
Residential,
};
public enum MyEnumType
{
[EnumMember]
First = 1,
[EnumMember]
Two = 2,
};
[DataContract(Namespace = Constants.DataNamespace)]
public enum Days
{
[EnumMember]
Sat = 1,
[EnumMember]
Sun,
[EnumMember]
Mon,
[EnumMember]
Tue,
[EnumMember]
Wed,
[EnumMember]
Thu,
[EnumMember]
Fri
};
[DataContract(Namespace = Constants.DataNamespace)]
public class PhoneNumber
{
public PhoneNumber()
{
}
public Guid Id { get; set; }
[DataMember]
public string FullNumber { get; set; }
[DataMember]
public PhoneType PhoneType { get; set; }
public Guid EntityId { get; set; }
}
[DataContract(Namespace = Constants.DataNamespace)]
public enum PhoneType
{
[EnumMember]
Tel = 0,
[EnumMember]
Mobile = 1,
[EnumMember]
Skype = 2,
[EnumMember]
Fax = 3,
}
[DataContract(Namespace = Constants.DataNamespace)]
public class Address
{
[DataMember]
public Guid Id { get; set; }
public Entity Entity { get; set; }
public Guid EntityId { get; set; }
[DataMember]
public string Street1 { get; set; }
[DataMember]
public string Street2 { get; set; }
[DataMember]
public string City { get; set; }
[DataMember]
public string State { get; set; }
[DataMember]
public string PostalCode { get; set; }
[DataMember]
public string Country { get; set; }
[DataMember]
public AddressType Type { get; set; }
[DataMember]
public DemoWebApi.DemoData.Another.MyPoint Location;
}
[DataContract(Namespace = Constants.DataNamespace)]
public class Entity
{
public Entity()
{
Addresses = new List<Address>();
}
[DataMember]
public Guid Id { get; set; }
[DataMember(IsRequired =true)]
[System.ComponentModel.DataAnnotations.Required]
public string Name { get; set; }
[DataMember]
public IList<Address> Addresses { get; set; }
[DataMember]
public virtual ObservableCollection<PhoneNumber> PhoneNumbers { get; set; }
public override string ToString()
{
return Name;
}
[DataMember]
public Uri Web { get; set; }
}
[DataContract(Namespace = Constants.DataNamespace)]
public class Person : Entity
{
[DataMember]
public string Surname { get; set; }
[DataMember]
public string GivenName { get; set; }
[DataMember]
public DateTime? DOB { get; set; }
public override string ToString()
{
return Surname + ", " + GivenName;
}
}
[DataContract(Namespace = Constants.DataNamespace)]
public class Company : Entity
{
[DataMember(Name ="BusinessNum")]
public string BusinessNumber { get; set; }
[DataMember]
public string BusinessNumberType { get; set; }
[DataMember]
public string[][] TextMatrix
{ get; set; }
[DataMember]
public int[][] Int2DJagged;
[DataMember]
public int[,] Int2D;
[DataMember]
public IEnumerable<string> Lines;
}
[DataContract(Namespace = Constants.DataNamespace)]
public class MyPeopleDic
{
[DataMember]
public IDictionary<string, Person> Dic { get; set; }
[DataMember]
public IDictionary<string, string> AnotherDic { get; set; }
[DataMember]
public IDictionary<int, string> IntDic { get; set; }
}
}
After running:
POCO2TS.exe demowebapi.demodata.dll TypeScriptDataModels.ts
or:
dotnet POCO2TSCore.dll demowebapi.demodatacore.dll TypeScriptDataModels.ts
The output TypeScriptDataModels.ts will contain TypeScript
interfaces:
export namespace DemoWebApi_DemoData_Client {
export enum AddressType { Postal, Residential }
export enum Days {
Sat = 1,
Sun = 2,
Mon = 3,
Tue = 4,
Wed = 5,
Thu = 6,
Fri = 7
}
export interface PhoneNumber {
fullNumber?: string;
phoneType?: DemoWebApi_DemoData_Client.PhoneType;
}
export enum PhoneType {
Tel,
Mobile,
Skype,
Fax
}
export interface Address {
id?: string;
street1?: string;
street2?: string;
city?: string;
state?: string;
postalCode?: string;
country?: string;
type?: DemoWebApi_DemoData_Client.AddressType;
location?: any;
}
export interface Entity {
id?: string;
name: string;
addresses?: Array<DemoWebApi_DemoData_Client.Address>;
phoneNumbers?: Array<DemoWebApi_DemoData_Client.PhoneNumber>;
web?: string;
}
export interface Person extends DemoWebApi_DemoData_Client.Entity {
surname?: string;
givenName?: string;
dob?: Date;
}
export interface Company extends DemoWebApi_DemoData_Client.Entity {
BusinessNum?: string;
businessNumberType?: string;
textMatrix?: Array<Array<string>>;
int2DJagged?: Array<Array<number>>;
int2D?: number[][];
lines?: Array<string>;
}
export interface MyPeopleDic {
dic?: {[id: string]: DemoWebApi_DemoData_Client.Person };
anotherDic?: {[id: string]: string };
intDic?: {[id: number]: string };
}
}
POCO2TS.exe as a command line program by default will just process all classes decorated by DataContractAttribute
and publish those members decorated by DataMemberAttribute
. However, members of enum
types will be all processed, regardless of EnumMemberAttribute
.
Foreign keys in Code First generally should not be exposed to client programs, and they are not decorated by DataMemberAttribute
, thus they are not included in the generated TypeScript interfaces.
Cherry Picking
As you can see, POCO2TS
uses primarily attributes to do cherry picking. There are multiple methods of cherry picking. If you run POCO2TS.exe without parameters, you may see the following:
Poco2Ts.exe generates TypeScript data model interfaces from POCO classes.
Example:
For classes decorated by DataContractAttribute:
Fonlow.Poco2Ts.exe MyAssemblyWithPOCO.dll MyOutputTS.ts
For classes decorated by Newtonsoft.Json.JsonObjectAttribute:
Fonlow.Poco2Ts.exe MyAssemblyWithPOCO.dll MyOutputTS.ts /2
For classes decorated by SerializableAttribute:
Fonlow.Poco2Ts.exe MyAssemblyWithPOCO.dll MyOutputTS.ts /4
For public classes, properties and properties,
and use System.ComponentModel.DataAnnotations.RequiredAttribute:
Fonlow.Poco2Ts.exe MyAssemblyWithPOCO.dll MyOutputTS.ts /8
For all classes, properties and fields
Fonlow.Poco2Ts.exe MyAssemblyWithPOCO.dll MyOutputTS.ts /0
So if you somehow have some classes decorated by DataContractAttribute
and others by JsonObjectAttribute
, and you want to include both sets, then the option is /3
. And DataMemberAttribute
and JsonPropertyAttribute
will be handled accordingly.
Points of Interest
POCO2TS
does not really have static dependency on Newtonsoft.Json. Only when you use some attribute classes like JsonObjectAttribute of NewtonSoft.Json to decorate your POCO classes and would use such attribute classes for cherry picking, Newtonsoft.Json is needed. When you use the NewtonsoftJson option, .NET runtime will try to locate it. If the runtime fails to locate it, POCO2TS
will resolve through checking the folder where the data model assembly is located. This resolution is also useful when your data model assembly has dependency on the other data model assembly.
You may have come across TypeLITE and TypeWriter, and there's a simple comparison: Compare with TypeLITE and TypeWriter.