Contents

ASP.NET 建立XML檔的方法

ASP.NET 建立XML檔的方法

產生XML檔,比較常見有兩種方法

  1. 使用XmlSerializer序列化物件
  2. 傳統的System.XML命名空間提供處理 XML 的標準架構支援目標,及XmlElement、XmlNode等

環境

  • XmlSerializer
  • LinqPad 5
Note
XmlSerializer 在LinqPad 6 .NET Core 3.1,可能會出現 NotSupportedException: A non-collectible assembly may not reference a collectible assembly. 錯誤,或許是下列問題 請參考 https://github.com/dotnet/runtime/issues/1388

XML常使用Stream作為輸出入格式,這邊為了方便,直接使用LinqPad中的Console.Out功能呈現結果

目標

官方XML範例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
<PurchaseOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.cpandl.com">
    <ShipTo Name="Teresa Atkinson">
        <Line1>1 Main St.</Line1>
        <City>AnyTown</City>
        <State>WA</State>
        <Zip>00000</Zip>
    </ShipTo>
    <OrderDate>Wednesday, June 27, 2001</OrderDate>
    <Items>
        <OrderedItem>
            <ItemName>Widget S</ItemName>
            <Description>Small widget</Description>
            <UnitPrice>5.23</UnitPrice>
            <Quantity>3</Quantity>
            <LineTotal>15.69</LineTotal>
        </OrderedItem>
    </Items>
    <SubTotal>15.69</SubTotal>
    <ShipCost>12.51</ShipCost>
    <TotalCost>28.2</TotalCost>
</PurchaseOrder>

實作

使用XmlSerializer序列化物件

建立類別檔,或先撰寫一份XML草稿,使用Visual Studio 的「貼上XML做為類別」功能,再整理一份比較常見的類別檔

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class PurchaseOrder
{
	public Address ShipTo { get; set; }
	public string OrderDate { get; set; }

	[XmlArray("Items")]
	public List<OrderedItem> OrderedItems { get; set; }
	public decimal SubTotal
	{
		get
		{
			return this.OrderedItems.Sum(x => x.LineTotal);
		}
		set { }
	}
	public decimal ShipCost { get; set; }
	public decimal TotalCost
	{
		get
		{
			return this.SubTotal + this.ShipCost;
		}
		set { }
	}
}
public class Address
{
	[XmlAttribute]
	public string Name { get; set; }
	public string Line1 { get; set; }
	public string City { get; set; }
	public string State { get; set; }
	public string Zip { get; set; }
}

public class OrderedItem
{
	public string ItemName { get; set; }
	public string Description { get; set; }
	public decimal UnitPrice { get; set; }
	public int Quantity { get; set; }
	// C# 7.0
	public decimal LineTotal
	{
		get => UnitPrice * Quantity;
		set { }
	}

	// XMLSerializer的侷限性,屬性沒有setter無法序列化
	// public decimal LineTotal => UnitPrice * Quantity;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void Main()
{
	var order = new PurchaseOrder
	{
		ShipTo = new Address
		{
			Name = "Teresa Atkinson",
			Line1 = "1 Main St.",
			City = "AnyTown",
			State = "WA",
			Zip = "00000",
		},
		OrderDate = DateTime.Now.ToString(),
		OrderedItems = new List<OrderedItem>
		{
			new OrderedItem
			{
				ItemName = "Widget S",
				Description = "Small widget",
				UnitPrice = (decimal)5.23,
				Quantity = 3
			}
		},
		ShipCost = (decimal)12.51,
	};

	// using System.Xml.Serialization
	XmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder));
	serializer.Serialize(Console.Out, order);
}
/static/ASP.NET_建立XML檔的方法_db501de3d05349debd7e8374a69d01f7/_2020-07-23_141225.png

使用XML命名空間

雖然命名空間提供的類別非常的多,但常用只有XmlElement、XmlNode、XmlReader、XmlWriter等

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
void Main()
{
	// 新增XML文件類別
	XmlDocument xmlDoc = new XmlDocument();
	// XML文件類別新增一個Element節點
	XmlElement order = xmlDoc.CreateElement("PurchaseOrder");
	xmlDoc.AppendChild(order);
	
	XmlElement address = xmlDoc.CreateElement("ShipTo");
	address.SetAttribute("Name","Teresa Atkinson"); // 設定屬性
	order.AppendChild(address);
	
	#region Address
	XmlElement line1 = xmlDoc.CreateElement("Line1");
	line1.InnerText = "1 Main St.";
	address.AppendChild(line1);
	XmlElement city = xmlDoc.CreateElement("City");
	city.InnerText = "AnyTown";
	// address節點底下
	address.AppendChild(city);
	XmlElement state = xmlDoc.CreateElement("State");
	state.InnerText = "WA";
	address.AppendChild(state);
	XmlElement zip = xmlDoc.CreateElement("Zip");
	zip.InnerText = "00000";
	address.AppendChild(zip);
	#endregion
	
	XmlElement orderDate = xmlDoc.CreateElement("OrderDate");
	orderDate.InnerText = DateTime.Now.ToString();
	order.AppendChild(orderDate);
	
	XmlElement items = xmlDoc.CreateElement("Items");
	order.AppendChild(items);
	XmlElement orderedItem = xmlDoc.CreateElement("OrderedItem");
	items.AppendChild(orderedItem);
	XmlElement itemName = xmlDoc.CreateElement("ItemName");
	itemName.InnerText = "Widget S";
	orderedItem.AppendChild(itemName);

	// 除了XmlElement外 也可以用XmlNode 兩者效果是一樣的
	XmlNode description = xmlDoc.CreateNode(XmlNodeType.Element,"Description",string.Empty);
	description.InnerText = "Small widget";
	orderedItem.AppendChild(description);
	
	// ...(以下略)
	
	XmlSerializer serializer = new XmlSerializer(typeof(XmlDocument));
	serializer.Serialize(Console.Out, xmlDoc);
}

補充

清掉 XML Namespace(xmlns)

1
2
// 就是這行中的xmlns
<PurchaseOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
1
2
3
4
5
6
7
XmlSerializerNamespaces emptyNS = new XmlSerializerNamespaces(new[] { new XmlQualifiedName("","") });
// 或
// XmlSerializerNamespaces emptyNS = new XmlSerializerNamespaces();
// emptyNS.Add("", "");

XmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder));
serializer.Serialize(Console.Out, order, emptyNS);

結論

因為JSON的普及,操作XML的機會越來越少,若單以建立XML檔,我優先選擇XmlSerializer,採用物件方式,程式碼較簡潔易懂,物件也易於後續的資料操作,而XmlDocument在修改、刪除節點內容時,個人覺得滿給力的

參考

余小章 @ 大內殿堂 - XML檔案基本操作-XmlDocument

https://dotblogs.com.tw/yc421206/2010/08/10/17108

Microsoft XML 序列化的範例

https://docs.microsoft.com/zh-tw/dotnet/standard/serialization/examples-of-xml-serialization

System.Xml 命名空間

https://docs.microsoft.com/zh-tw/dotnet/api/system.xml?view=netcore-3.1

檔案

使用XmlSerializer序列化物件.linq

使用XML命名空間.linq