為了開發地址輸入控件,需要台灣地區郵遞區號及地址路名的基本資料。中華郵政網站提供了完整的3+2碼式郵遞區號對照表可供下載(3+2郵遞區號資料XML檔(自解壓縮檔) 98/11 ),是絕佳的權威資料來源:

<NewDataSet>
  <zip32>
    <zipcode>10058</zipcode>
    <city>台北市</city>
    <area>中正區  </area>
    <road>八德路1段            </road>
    <scoop></scoop>
  </zip32>
  <zip32>
    <zipcode>10070</zipcode>
    <city>台北市</city>
    <area>中正區  </area>
    <road>三元街                </road>
    <scoop>雙  48號以下                        </scoop>
  </zip32>
  <zip32>
    <zipcode>10079</zipcode>
    <city>台北市</city>
    <area>中正區  </area>
    <road>三元街                </road>
    <scoop>雙  50號以上                        </scoop>
  </zip32>

不過,依手邊的應用上,只打算使用三碼郵遞區號及路名提示,因此打算將資料整成以下的格式,只保留必要資訊,降低檔案大小(原本高達12MB)也提升查詢效率:

<?xml version="1.0" encoding="utf-8"?>
<AddressData>
  <Zip T="100">
    <City T="台北市">
      <Area T="中正區">
        <Road T="八德路1段" />
        <Road T="三元街" />
        <Road T="中山北路1段" />
        <Road T="仁愛路2段" />
        <Road T="公園路" />
        <Road T="水源路" />
        <Road T="汀州路2段" />
        <Road T="汀州路3段" />
        <Road T="和平西路1段" />

試寫了一下,程式比預期的簡單。藉著Extension MethodLINQ to XML的協助,只花了不到60行,執行不到10秒鐘,就算出想要的結果,XML檔案也由原先的12MB,如期縮小到1.2MB。

程式碼分享如下,其中root.CreateOrGetElement(...).CreateOrGetElement(…).CreateOrGetElement(…)的寫法頗為有趣,很有寫jQuery的fu~~~

using System.Linq;
using System.Xml.Linq;
 
namespace CompactZipXml
{
    class Program
    {
        static void Main(string[] args)
        {
            XDocument xdRaw = 
                XDocument.Load("C:\\Temp\\zip32_9811.xml");
            XDocument xdResult = 
                XDocument.Parse("<AddressData />");
            XElement root = xdResult.Root;
            
            //記錄目前的Area XML節點
            XElement currentArea = null;
            
            //逐一處理所有zip32
            foreach (var z in xdRaw.Root.Elements().OrderBy(o => o.Element("city").Value).OrderBy(o => o.Element("area").Value))
            {
                //檢查目前區域,若不同則新建,相同則繼續沿用
                string area = z.Element("area").Value.Trim();
                if (currentArea == null ||
                    currentArea.Attribute("T").Value != area)
                {
                    currentArea = root
                        .CreateOrGetElement("Zip", //只取3碼
                           z.Element("zipcode").Value.Substring(0, 3))
                        .CreateOrGetElement("City", 
                           z.Element("city").Value.Trim())
                        .CreateOrGetElement("Area", area);
                }
                currentArea.CreateOrGetElement("Road", 
                        z.Element("road").Value.Trim());
            }
 
            xdResult.Save("C:\\TEMP\\zip3.xml");
        }
    }
 
    public static class ZipXmlExt
    {
        //利用Extension Method功能,為XEelement新增或取得特定子元素
        public static XElement CreateOrGetElement(this XElement parent, 
            string elemName, string attValue)
        {
            var q = parent.Elements(elemName)
                    .Where(o => o.Attribute("T").Value == attValue)
                    .SingleOrDefault();
            if (q != null)
                return q;
            else
            {
                XElement elem = new XElement(elemName);
                elem.SetAttributeValue("T", attValue);
                parent.Add(elem);
                return elem;
            }
        }
    }
}

Comments

# by blackie

太棒了~感謝貢獻

Post a comment