ezTRACK 是一家全球性的RFID、條碼追蹤電子交換平台,然而EPCIS即是電子產品程式碼資訊服務(Electronic Product Code Information Services),為EPC資料提供了一整套標準介面,今天重點不是它們兩個,而是ezTRACK所給我的文件,我要批次產生文件然後上傳到它們的Server,以下就是ezTrack的Xml文件範本:
<?xml version="1.0" encoding="utf-8"?> <epcis:EPCISDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sensor="http://epcis.gs1hk.org/ns/sensor" xmlns:gs1hk="urn:epcglobal:gs1hk:xsd:ext" schemaVersion="1" creationDate="2010-09-15T04:07:52" xmlns:epcis="urn:epcglobal:epcis:xsd:1"> <EPCISBody> <EventList> <ObjectEvent> <eventTime>2011-12-25T17:04:52</eventTime> <eventTimeZoneOffset>+08:00</eventTimeZoneOffset> <epcList> <epc>BBAA99887766554400085172</epc> <epc>BBAA99887766554400085173</epc> </epcList> <action>OBSERVE</action> <bizStep>urn:epcglobal:fmcg:bizstep:Gate_In</bizStep> <bizLocation> <id>urn:epcglobal:fmcg:loc.MMMMM.202.MZMFG_Warehouse_Gate_Door,414.Gate_Door2</id> </bizLocation> <bizTransactionList> <bizTransaction type="urn:gs1hk:ext:order">China</bizTransaction> </bizTransactionList> <sensor:temperature>20.5</sensor:temperature> <sensor:state>Alram</sensor:state> <sensor:temperaturereporturl>http://www.gts-portal.co/Z/Page/TempChart.aspx?EPC=BBAA99887766554400085172&Date=2011-12-25T17:04:52</sensor:temperaturereporturl> </ObjectEvent> </EventList> </EPCISBody> </epcis:EPCISDocument>
一看到這個文件時,我就想要利用XSD.exe來處理(請參考[XML] XSD Convert XML),於是就去找它的schema快速處理http://www.epcglobalinc.org/standards/epcis/epcis_1_0-schema-20070412.zip,但一直沒有辦法成功,兩岸三地的高手都說xsd檔有問題
http://social.msdn.microsoft.com/Forums/zh-TW/239/thread/8e62987a-1e67-4feb-8856-ee801ee2baa2
於是乎動手自己寫Entity Class來序列化為Xml念動便油然而生,這得定義Xml Attribute,我們先拆成小塊處理,雖然實作過程有碰到些問題,但總算解決了。
首先是bizLocation區塊的定義,我猜想id結構應該會是集合,所以用List<string>
/// <summary> /// 位置 /// </summary> [Serializable, XmlRoot("bizLocation")] public class BizLocation { /// <summary> /// 位置ID /// </summary> private List<string> _Ids= new List<string>(); [XmlElement("id")] public List<string> Ids { get { return _Ids; } set { _Ids = value; } } }
epcList我則是指定XmlArrayItem屬性,這跟上述bizLocation屬性最後產生的結果會一樣
private List<string> _epcList = new List<string>(); /// <summary> /// EPC 編號清單 /// EPC List /// </summary> [XmlArrayItem("epc")] public List<string> epcList { get { return this._epcList; } set { this._epcList = value; } }
bizTransaction裡有XmlAttribute type,所以要特別處理下,Content則是用來裝BizTransaction用
/// <summary> /// 交易內容 /// </summary> [Serializable, XmlRoot("bizTransaction")] //[Serializable] public class BizTransaction { private string _type = "urn:gs1hk:ext:order"; /// <summary> /// 交易內容屬性 /// </summary> [XmlAttribute("type")] public string type { get { return this._type; } set { this._type = value; } } /// <summary> /// 內容 /// </summary> [XmlText()] public string Content { get; set; } }
sensor 指的是xml的命名空間,所以需要為這些屬性定義Namespace
/// <summary> /// Tag 溫度 /// </summary> [XmlElement("temperature", Namespace = "http://epcis.gs1hk.org/ns/sensor")] public string Temperature { get; set; } /// <summary> /// Tag 的狀態 /// </summary> [XmlElement("state", Namespace = "http://epcis.gs1hk.org/ns/sensor")] public string State { get; set; } /// <summary> /// temperaturereporturl 超連結 /// </summary> [XmlElement("temperaturereporturl", Namespace = "http://epcis.gs1hk.org/ns/sensor")] public string TemperaturerEportUrl { get; set; }
整個ObjectEvent結構在類別裡的定義如下:
/// <summary> /// 事件類型 /// </summary> /// <seealso cref="ObjectEvent"/> [Serializable, XmlRoot("ObjectEvent")] public class ObjectEvent { #region 固定欄位 #region EventTime /// <summary> /// Tag 當時的時間 /// Event Time /// </summary> [XmlElement("eventTime")] public string EventTime { get; set; } #endregion #region EventTimeZoneOffset /// <summary> /// Time Zone offset /// 時差 /// </summary> [XmlElement("eventTimeZoneOffset")] public string EventTimeZoneOffset { get; set; } #endregion #region epcList private List<string> _epcList = new List<string>(); /// <summary> /// EPC 編號清單 /// EPC List /// </summary> [XmlArrayItem("epc")] public List<string> epcList { get { return this._epcList; } set { this._epcList = value; } } #endregion #region action /// <summary> /// 事件動作 /// Action /// </summary> [XmlElement("action")] public string EventAction { get; set; } #endregion #region bizStep /// <summary> /// 商業流程 /// BizStep /// </summary> [XmlElement("bizStep")] public string BizStep { get; set; } #endregion #region disposition /// <summary> /// 目前配置 /// Disposition /// </summary> [XmlElement("disposition")] public string Disposition { get; set; } #endregion #region bizLocation private BizLocation _BizLocation = new BizLocation(); /// <summary> /// 位置 /// bizLocation /// </summary [XmlElement("bizLocation")] public BizLocation BizLocation { get { return this._BizLocation; } set { this._BizLocation = value; } } #endregion #region bizTransaction private BizTransactionList _BizTransactionList = new BizTransactionList(); /// <summary> /// 交易清單 /// </summary> [XmlElement("bizTransactionList")] public BizTransactionList BizTransactionList { get { return this._BizTransactionList; } set { this._BizTransactionList = value; } } #endregion #endregion #region 可擴充欄位 /// <summary> /// Tag 溫度 /// </summary> [XmlElement("temperature", Namespace = "http://epcis.gs1hk.org/ns/sensor")] public string Temperature { get; set; } /// <summary> /// Tag 的狀態 /// </summary> [XmlElement("state", Namespace = "http://epcis.gs1hk.org/ns/sensor")] public string State { get; set; } /// <summary> /// temperaturereporturl 超連結 /// </summary> [XmlElement("temperaturereporturl", Namespace = "http://epcis.gs1hk.org/ns/sensor")] public string TemperaturerEportUrl { get; set; } #endregion }
接著是ObjectEvent上層EventList
/// <summary> /// 事件清單 /// </summary> [Serializable, XmlRoot("EventList")] public class EventList { private ObjectEvent _ObjectEvent = new ObjectEvent(); /// <summary> /// 事件類型 /// </summary> [XmlElement("ObjectEvent")] public ObjectEvent ObjectEvent { get { return this._ObjectEvent; } set { this._ObjectEvent = value; } } }
/// <summary> /// EPCIS 內文 /// </summary> [Serializable, XmlRoot("EPCISBody")] public class Body { EventList _EventList = new EventList(); /// <summary> /// 事件清單 /// </summary> [XmlElement("EventList")] public EventList EventList { get { return this._EventList; } set { this._EventList = value; } } }
EPCISBody的上層EPCISDocument
/// <summary> /// EPCIS Root /// </summary> [Serializable, XmlRoot("EPCISDocument", Namespace = "urn:epcglobal:epcis:xsd:1")] public class EPCISDocument { private Body _EPCISBody = new Body(); /// <summary> /// EPCIS 內容 /// </summary> [XmlElement(ElementName = "EPCISBody", Namespace = "")] public Body EPCISBody { get { return this._EPCISBody; } set { this._EPCISBody = value; } } private decimal _SchemaVersion = (decimal)1.0; /// <summary> /// 結構版本 /// </summary> [XmlAttribute("schemaVersion")] public decimal SchemaVersion { get { return this._SchemaVersion; } set { this._SchemaVersion = value; } } private string _CreationDate = "2010-09-15T04:07:52"; /// <summary> /// 認証日期 /// </summary> [XmlAttribute("creationDate")] public string CreationDate { get { return this._CreationDate; } set { this._CreationDate = value; } } }
剛剛我們在一些屬性定義了Xml Namespace,如下圖:
還要設定XmlSerializerNamespaces 類別,等會序列化時會用的到
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces(); namespaces.Add("epcis", "urn:epcglobal:epcis:xsd:1"); namespaces.Add("sensor", "http://epcis.gs1hk.org/ns/sensor"); namespaces.Add("gs1hk", "urn:epcglobal:gs1hk:xsd:ext"); namespaces.Add("xsi", http://www.w3.org/2001/XMLSchema-instance);
然後再為這些欄位定義一個Config類別,用它來對外公開主要設定結構欄位有哪些
/// <summary> /// EPSIC設定檔 /// </summary> [Serializable] public class EPCISConfig { #region FileName /// <summary> /// 完整檔案路徑 /// </summary> public string FileName { get; set; } #endregion #region EventTime private DateTime? _EventTime = null; /// <summary> /// UTC時間 /// </summary> public DateTime? EventTime { get { //if (this._EventTime == null) // this._EventTime = DateTime.Now; return this._EventTime; } set { this._EventTime = value; } } #endregion #region EventTimeZoneOffset private string _EventTimeZoneOffset = null; /// <summary> /// 時差 /// </summary> public string EventTimeZoneOffset { get { if (string.IsNullOrEmpty(this._EventTimeZoneOffset)) { int day = TimeZoneInfo.Local.BaseUtcOffset.Days; int hours = TimeZoneInfo.Local.BaseUtcOffset.Hours; int minutes = TimeZoneInfo.Local.BaseUtcOffset.Minutes; string localZone = ""; if (hours > 0) localZone = string.Format("+{0}:{1}", hours.ToString().PadLeft(2, '0'), minutes.ToString().PadLeft(2, '0')); else if (hours < 0) localZone = string.Format("-{0}:{1}", hours.ToString().PadLeft(2, '0'), minutes.ToString().PadLeft(2, '0')); this._EventTimeZoneOffset = localZone; } return this._EventTimeZoneOffset; } set { this._EventTimeZoneOffset = value; } } #endregion #region EpcList private List<string> _EpcList = new List<string>(); /// <summary> /// EPC 清單 /// </summary> public List<string> EpcList { get { return this._EpcList; } set { this._EpcList = value; } } #endregion #region Action private string _Action; /// <summary> /// 事件動作 /// Action /// </summary> public string Action { get { return this._Action; } set { this._Action = value; } } #endregion #region BizStep /// <summary> /// 商業流程 /// </summary> public string BizStep { get; set; } #endregion #region Disposition /// <summary> /// 目前配置 /// </summary> public string Disposition { get; set; } #endregion #region BizTransactionList private List<string> _TransactionList = new List<string>(); /// <summary> /// 交易清單 /// </summary> public List<string> TransactionList { get { return this._TransactionList; } set { this._TransactionList = value; } } #endregion #region LocationList private List<string> _LocationList = new List<string>(); /// <summary> /// 位置清單 /// </summary> public List<string> LocationList { get { return _LocationList; } set { _LocationList = value; } } #endregion #region 擴充欄位 #region CurrentTemperature /// <summary> /// 目前時間 /// </summary> public string Temperature { get; set; } #endregion #region CurrentState /// <summary> /// 目前狀態 /// </summary> public string State { get; set; } #endregion #region TemperatureURL /// <summary> /// 溫度查詢網址 /// </summary> public string TemperatureURL { get; set; } #endregion #endregion }
Document工廠類別則是用來實體化EPCISDocument 以及建立實體Xml檔案
public class Document { public Document(EPCISConfig Config) { if (Config == null) throw new ArgumentNullException("Config"); if (string.IsNullOrEmpty(Config.FileName)) throw new ArgumentNullException("Config.FileName"); XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces(); namespaces.Add("epcis", "urn:epcglobal:epcis:xsd:1"); namespaces.Add("sensor", "http://epcis.gs1hk.org/ns/sensor"); namespaces.Add("gs1hk", "urn:epcglobal:gs1hk:xsd:ext"); namespaces.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance"); EPCISDocument doc = this.createEpcIsDocument(Config); this.ToXml(Config.FileName, doc, namespaces); } EPCISDocument createEpcIsDocument(EPCISConfig Config) { EPCISDocument epcisDocument = new EPCISDocument(); string eventTime = string.Format("{0:s}", (DateTime)Config.EventTime); epcisDocument.EPCISBody.EventList.ObjectEvent.EventTime = eventTime; epcisDocument.EPCISBody.EventList.ObjectEvent.EventTimeZoneOffset = Config.EventTimeZoneOffset; epcisDocument.EPCISBody.EventList.ObjectEvent.epcList = Config.EpcList; epcisDocument.EPCISBody.EventList.ObjectEvent.EventAction = Config.Action; epcisDocument.EPCISBody.EventList.ObjectEvent.BizStep = Config.BizStep; epcisDocument.EPCISBody.EventList.ObjectEvent.Disposition = Config.Disposition; epcisDocument.EPCISBody.EventList.ObjectEvent.BizLocation.Ids = Config.LocationList; foreach (var item in Config.TransactionList) { epcisDocument.EPCISBody.EventList.ObjectEvent.BizTransactionList.BizTransaction.Add(new BizTransaction() { Content = item }); } epcisDocument.EPCISBody.EventList.ObjectEvent.State = Config.State; epcisDocument.EPCISBody.EventList.ObjectEvent.Temperature = Config.Temperature; epcisDocument.EPCISBody.EventList.ObjectEvent.TemperaturerEportUrl = Config.TemperatureURL; return epcisDocument; } void ToXml(string FileName, object Object, XmlSerializerNamespaces Namespace) { XmlSerializer xml = null; Stream stream = null; StreamWriter writer = null; try { xml = new XmlSerializer(Object.GetType()); stream = new FileStream(FileName, FileMode.Create, FileAccess.Write, FileShare.Read); writer = new StreamWriter(stream, Encoding.UTF8); xml.Serialize(writer, Object, Namespace); } catch { throw; } finally { if (writer != null) writer.Close(); if (stream != null) stream.Close(); } } }
在客戶端,定義EPCISConfig ,並實體化Document 即可
private void button1_Click(object sender, EventArgs e) { string file = Application.StartupPath + "\\" + "Test.xml"; EPCISConfig config = new EPCISConfig(); DateTime date = DateTime.Now.ToUniversalTime(); string time = string.Format("{0:s}", (DateTime)date); string epc = "BBAA99887766554400085172"; config.FileName = file; //實體化Config並帶入相關欄位 config.BizStep = "urn:epcglobal:fmcg:bizstep:Gate_In"; config.State = "Alram"; config.Action = "OBSERVE"; config.Temperature = "20.5"; config.EventTime = date; config.LocationList.Add("urn:epcglobal:fmcg:loc.MMMMM.202.MZMFG_Warehouse_Gate_Door,414.Gate_Door2"); string url = string.Format("http://www.gts-portal.co/Z/Page/TempChart.aspx?EPC={0}&Date={1}", epc, time); config.TemperatureURL = url; config.EpcList.Add(epc); config.EpcList.Add("BBAA99887766554400085173"); config.TransactionList.Add("China"); Document doc = new Document(config); }
在實作這個Xml時我有碰到一些問題,這些問題我在半年多前就已經透過MSDN論壇解決,感謝兩岸三地的高手協助,今日特與分享實作結果
http://social.msdn.microsoft.com/Forums/zh-TW/239/thread/2c8d5930-e5ea-4f6a-8987-f19bd589af22
範例下載: