C# example - xml serialization and xml tree

Serialization

Due to the existence of pointer and reference types, in a running program, the data is not necessarily a whole block.
It may be scattered in various places in the memory.

Sequence refers to a continuous and orderly whole. Serialization is the process of turning data into a continuous and ordered whole.
The data processed in this way can be conveniently transmitted and stored.

xml serialization

xml format

xml is a text data format. The name of the data and the content of the data are represented in the form of a node tree.
In c#, time, numbers, strings and other basic types have built-in ways to convert directly with strings.
The complex type will disassemble its members through reflection until there are only basic types.

public class Weapon//自带的序列化api要求类是public的。
{
    
    
	public (int, int) Attack {
    
     get; set; }
	public float Speed {
    
     get; set; }
	public int Level {
    
     get; set; }
}
<?xml version="1.0" encoding="utf-16"?>
<Weapon xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Attack>
    <Item1>10</Item1>
    <Item2>20</Item2>
  </Attack>
  <Speed>1.5</Speed>
  <Level>3</Level>
</Weapon>

serialization api

The standard library only provides System.Xml.Serializationserialization APIs given by the namespace.
This api cannot be directly serialized as a string type. Can only be written to the stream.
But we can use StringWriterand StringRead, two things that disguise strings as streams for him to write.

Weapon weapon = new Weapon() {
    
     Attack = (10, 20), Speed = 1.5f, Level = 3 };

// 创建一个XmlSerializer对象,传入Person类型
XmlSerializer xs = new XmlSerializer(typeof(Weapon));

// 创建一个StringWriter对象,用于保存XML字符串
StringWriter sw = new StringWriter();

// 调用XmlSerializer的Serialize方法,把Person对象序列化为XML字符串,并写入StringWriter对象
xs.Serialize(sw, weapon);

// 从StringWriter对象中获取XML字符串
string xml = sw.ToString();

// 输出XML字符串
Console.WriteLine(xml);

To simplify this process, extension methods can be made.

public static class Extension
{
    
    
	/// <summary>
	/// 将对象xml序列化为字符串
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="value"></param>
	/// <returns></returns> 
	public static string XmlSerialize<T>(this T value)
	{
    
    
		XmlSerializer xml = new XmlSerializer(typeof(T));
		StringWriter sw = new StringWriter();
		xml.Serialize(sw, value);
		return sw.ToString();
	}

	/// <summary>
	/// 将xml字符串反序列化
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="value"></param>
	/// <returns></returns>
	public static T XmlDeSerialize<T>(this string value)
	{
    
    
		XmlSerializer xml = new XmlSerializer(typeof(T));
		StringReader sr = new StringReader(value);
		return (T)xml.Deserialize(sr);
	}
}

Attributes Control Serialization Rules

This serialization api

  • It is required that the target class must be publicmodified.
  • He will only publicserialize the members, including fields and properties.
  • It requires the target type to have a public no-argument constructor
  • Assignment through reflection, if the target property does not have a set accessor, or does not have an element with the same name, the target property will keep the default value.

Some attributes can control its serialization rules.

element

XmlElementAttributes can specify element names.
When used on an array or collection, it will be flattened into elements.

before using the array

<Person>
  <Hobbies>
    <string>读书</string>
    <string>写作</string>
    <string>编程</string>
  </Hobbies>
</Person>

After using the array

<Person>
  <Hobbies>读书</Hobbies>
  <Hobbies>写作</Hobbies>
  <Hobbies>编程</Hobbies>
</Person>

Attributes

XmlAttributeAttributes allow a member to be serialized as an xml attribute.
This requires that it cannot be a composite type, and must be a type like int, bool, and string that can be directly represented by a string without splitting.

before use on members

<Person>
  <Age>20</Age>
</Person>

after using on members

<Person Age="20" />

text

XmlTextAttributes allow a member to be serialized as a text node.
Because text nodes cannot be distinguished, there can only be at most one member of a class with this property.
before use on members

<Person>
  <Age>20</Age>
</Person>

after using on members

<Person>
  20
</Person>

neglect

Members with XmlIgnoreattributes are ignored during serialization and deserialization.

to sort

Attributes and attributes of elements, attributes or elements can be named.
In addition, the element has an optional Order attribute, which can control the order of serialization.
But either none of them have this attribute, or they all explicitly declare this attribute.

public class Person
{
    
    
	[XmlElement("姓名",Order =1)]
	public string Name {
    
     get; set; }
	[XmlAttribute("年龄")]
	public int Age {
    
     get; set; }
	[XmlElement(Order =0)]
	public string[] Hobbies {
    
     get; set; }
}

polymorphism

XML can represent more information, so that polymorphism can be preserved.
Using XmlArrayItema specified type for an array or collection can identify the type during deserialization
(whether the type is saved during serialization).
But you need to predict all possible types in advance and specify them one by one.

public class Data
{
    
    
	[XmlArrayItem("字符串", typeof(string))]
	[XmlArrayItem("数字", typeof(int))]
	public object[] Datas;
}

More

See Using attributes to control XML serialization

xml tree

For the existing xml string, you can use it System.Xml.Linqunder the namespace XElement.Parsefor parsing.
For things like files, streams, you can use XElement.Loadread loading (parameter is stream or path).

An XElement instance can be used to ToStringview its xml string, saved as a file or written to a stream.
Save

xml node

The content of the xml tree is very large, and there are the following types according to the inheritance chain.

  • XObject
    • XAttribute
    • XNode
      • XComment
      • XDocumentType
      • XProcessingInstruction
      • XText
        • XCData
      • XContainer
        • XDocument
        • XElement
<?xml version="1.0" encoding="utf-8"?>
<!-- 这是一个处理指令(XProcessingInstruction),用于声明 XML 文档的版本、编码等信息 -->
<!-- This is a comment -->
<!-- 这是一个注释(XComment),用于添加一些说明性的文本 -->
<!DOCTYPE Root [
	<!-- 这是一个文档类型声明(XDocumentType),用于定义 XML 文档的结构和约束 -->
	<!ELEMENT Root (Child1, Child2)>
	<!-- 这是一个元素类型声明,用于指定 Root 元素的内容模型 -->
	<!ATTLIST Root id ID #REQUIRED>
	<!-- 这是一个属性列表声明,用于指定 Root 元素的属性 -->
]>
<Root id="R1">
	<!-- 这是一个元素(XElement),表示 XML 文档的根元素,它有一个属性(XAttribute) id,值为 R1 -->
	<Child1>Some text</Child1>
	<!-- 这是一个元素(XElement),表示 Root 元素的第一个子元素,它有一些文本内容(XText) -->
	<Child2 att="A1"/>
	<!-- 这是一个元素(XElement),表示 Root 元素的第二个子元素,它有一个属性(XAttribute) att,值为 A1 -->
	<Child3><![CDATA[在这里可以输入<>,xml,!!]]></Child3>
	<!--这是一个元素(XElement),他里面有一个CData,表示不会被转义的文本。-->
</Root>

Attributes

Attributes are things on an element in the form of key-value pairs.
Attributes can only store plain text information, and cannot represent content with hierarchical relationships.

XElement xel = new XElement("ele");
Console.WriteLine(xel);
var xat = new XAttribute("name", "张三");
xel.Add(xat);
Console.WriteLine(xel);
<ele />
<ele name="张三" />

Methods can be invoked from XElement instances Attributeto query properties of the specified name.
You can modify it from the obtained Attribute, or you can directly set the attribute with the specified name from the XElement.

XElement xel2 = XElement.Parse(@"<ele name=""张三"" />"); 
var xat2=xel2.Attribute("name");
xat2.Value = "999";//Value只能是string类型
Console.WriteLine(xel2);

xel2.SetAttributeValue("name",true);//这个可以是任意类型,如果是null则会删除这个属性。
Console.WriteLine(xel2);

Console.WriteLine(xat2);
<ele name="999" />
<ele name="true" />
name="true"

Attributes can also be converted to basic types such as strings, numbers, and times through coercion.

XAttribute xat3 = new XAttribute("name", "16");
float? f = (float?)xat3;
Console.WriteLine(f);

The basic type refers to the type defined in the xml format. It is the basic type of xml rather than the basic type of c#.

note

In xml, the used <!--and -->surrounded parts are comments. No formatting is required within comments.

XComment comment = new XComment("这个是注释");
Console.WriteLine(comment);
Console.WriteLine("================");
XElement xel = new XElement("root",comment); 
Console.WriteLine(xel);
<!--这个是注释-->
================
<root>
  <!--这个是注释-->
</root>

Annotations do not save data information, so they cannot be used to parse data like attributes.

document type

A document type is a specification that provides document node validation.
Often an external file is referenced to require the xml to conform to the format.
If it is inlined into the xml tree, it is as follows.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
  <to>Tove</to>
  <from>Jani</from>
  <heading>Reminder</heading>
  <body>Don't forget me this weekend!</body>
</note>

He requires that the note element must contain to, from, heading, and body elements.
Then to, from, heading, and body must all be parsable text (cannot contain nested elements).

Obtaining document constraints needs to be obtained by parsing the document.

XDocument xDoc = XDocument.Parse(xml);//xml替换为上述的字符串

var xdt = xDoc.DocumentType; 
Console.WriteLine(xdt.InternalSubset);

There are special methods for verification, and there will be no exceptions during parsing.
But how to verify I did not find out.

process command

The processing command is the content between <?to ?>. The text
immediately to the left will be parsed as the target (Target). Then all the content after a space (even if there are still spaces to split multiple blocks) are all data (Data).<?

XElement note = XElement.Parse(@"
<场景>
  <对话 text=""您想要什么?"">
    <回答 text=""红宝石"">
      <?属性 攻击力 1?>
    </回答>
    <回答 text=""蓝宝石"">
      <?属性 防御力 1?>
    </回答>
    <回答 text=""血瓶"">
      <?属性 生命 100?>
    </回答>
  </对话>
</场景>
");
var p1 = (XProcessingInstruction)note.Elements().First().Elements().First().FirstNode;
Console.WriteLine(p1.Target);//属性
Console.WriteLine(p1.Data);//攻击力 1

text

The text under the element is a text node.

XElement note = XElement.Parse(@"
<root>
  你好,世界
</root>
");
var t1 =(XText) note.FirstNode;

Console.WriteLine(t1.Value);

But as long as there is no opening and closing tag that fits the element, it will be included.
For example, there is a line break between the label and the text here. So the text content here will contain line breaks.

escaped text

Normally, xml content cannot contain angle brackets, quotation marks and the like. If you want to write, you need to escape.
But if it is a text element, you can <![CDATA[]]>escape the text by declaring it.
Because he has a lot of opening and closing symbols. So please take a look at it usually, there will be no ambiguous interpretation of the things inside.

XElement note = XElement.Parse(@"
<root>
  <![CDATA[这是一个xml的标签:<a int=""3"" />]]>
</root>
");
var t1 =(XCData) note.FirstNode;

Console.WriteLine(t1.Value);

This node can only be a text node, so this type is XTextderived from .

document

The common base class for documents and elements XContaineris what may nest things inside.
It is only used to parse an element, both documents and elements can be used.
But documents may additionally contain specifiers starting with xml.
<?xml version="1.0" encoding="UTF-8"?>This represents XML using 1.0 syntax, encoded using UTF-8.

XDeclaration declaration = new XDeclaration("1.0", "UTF-8", null);

element

The xml element is the most important thing in the xml tree, which can represent levels, contain content, and carry attributes.
An element can nest multiple elements with the same name, so unlike json, you can use an indexer to access the content.
The following examples show common methods of xml elements.

XElement letters = XElement.Parse(@"
<letters>
  <letter from=""张三"" to=""李四"" date=""2022-01-30"">
    <subject>问候</subject>
    <body>李四,你好!最近过得怎么样?</body>
  </letter>
  <letter from=""李四"" to=""张三"" date=""2022-02-01"">
    <subject>回复</subject>
    <body>张三,你好!我最近很好,谢谢你的关心。</body>
  </letter>
</letters>
");
foreach (var item in letters.Descendants().Where(x=>x.Name=="subject"))
{
    
    //Descendants方法可以递归获取所有子节点。
	Console.WriteLine(item.Value);
}

Console.WriteLine(letters.Elements("letter").First().Value);
//Elements方法为获取所有指定名字的直属子节点。可以不填名字。

letters.SetAttributeValue("count",4);
//SetAttributeValue方法可以修改属性的值,如果没有这个属性会添加。如果使用null值会移除属性。

Elements can use Addmethods, or add multiple values ​​after the constructor name.

  • If it is of the XObject family, it will be embedded in the tree.
  • For arrays, things of iterable type will be disassembled and added to the elements in order.
  • Other types of data will be added after converting the text using ToString. So complex types need to be serialized first, then parsed into xml nodes, and then add xml nodes.

XName

Although in the above example, the names of attributes and nodes are directly using the string type.
But in fact, the constructor accepts the XName type, which is subdivided into namespace and name.

XElement xel = new XElement("{火蜥蜴战队}张三");
Console.WriteLine(xel);
//<张三 xmlns="火蜥蜴战队" />

XName xn = xel.Name;
Console.WriteLine(xn.NamespaceName); //火蜥蜴战队
Console.WriteLine(xn.LocalName);     //张三

The constructor of the XName type is private. But there is an implicit conversion from string.
In the if statement, it can be used directly ==for judgment,
but in the switch statement, its name needs to be accessed from its properties.
Because implicit conversion is an operation process, not a constant, it cannot be used for switch.

When a node has a namespace, its child nodes default to the same namespace as it.
Therefore, different namespaces must be identified, including namespaces "".

XElement xel = new XElement("{火蜥蜴战队}张三"
	, new XElement("李四")
	, new XElement("{不死鸟战队}王五")
	, new XElement("{火蜥蜴战队}赵六"));
Console.WriteLine(xel);
/*
<张三 xmlns="火蜥蜴战队">
  <李四 xmlns="" />
  <王五 xmlns="不死鸟战队" />
  <赵六 />
</张三>
*/

When used on attributes, an additional namespace attribute will be declared.
This extra namespace attribute can be changed.

XElement xel = new XElement("{火蜥蜴战队}张三"
	, new XAttribute("{2023}职务","队长")
	, new XElement("李四"
		,new XAttribute("{2023}职务","副队长")
	)
); 
Console.WriteLine(xel);
/*
<张三 p1:职务="队长" xmlns:p1="2023" xmlns="火蜥蜴战队">
  <李四 p1:职务="副队长" xmlns="" />
</张三>
*/
Console.WriteLine("===========");
xel.SetAttributeValue(XNamespace.Xmlns + "zhang", "2023");
//XNamespace.Xmlns 这个静态变量是声明命名空间的xml命名空间。和一个字符串相加以后会变成XName
Console.WriteLine(xel);
/*
<张三 zhang:职务="队长" xmlns:zhang="2023" xmlns="火蜥蜴战队">
  <李四 zhang:职务="副队长" xmlns="" />
</张三>
*/

Guess you like

Origin blog.csdn.net/zms9110750/article/details/131715224