[C#.NET][XML] 處理 ezTRACK 的 EPCIS Xml文件
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; }
}
}
還有EventList的上層EPCISBody
/// <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
範例下載:
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET