当前位置: 首页 > 工具软件 > Metah.X > 使用案例 >

Metah.X: An XML Metaprogramming Language

唐阳飙
2023-12-01
Metah.X(简称MX)用自创的语法实现了[url="http://www.w3.org/TR/xmlschema-1/"]XML Schema 1.0[/url]的语义,并且用C#实现了一个Schema-lized Document Object Model (SDOM),编译器编译MX代码后将生成使用SDOM的C#代码,这将XML Schema的语义映射到C#上,从而完全释放出XML Schema的力量。尽管现在只有C#版,实现Java版或其它语言版本是完全可能的。
MX是个开源项目,欢迎参与,比如实现Java版或其它语言版本;MX没有定型,欢迎提出修改意见。请访问:[url]http://metah.codeplex.com/[/url]。
XML Schema定义了XML数据(或叫XML实例)的形状及需要遵守的规则,比如下面的XSD代码:

<?xml version="1.0" encoding="utf-8"?>
<!--HelloWorld.xsd-->
<schema targetNamespace="http://schemas.example.com/projecta" elementFormDefault="qualified"
xmlns:tns="http://schemas.example.com/projecta" xmlns="http://www.w3.org/2001/XMLSchema">
<simpleType name="String20">
<restriction base="string">
<minLength value="1"></minLength>
<maxLength value="20"></maxLength>
</restriction>
</simpleType>
<simpleType name="PhoneCategory">
<restriction base="string">
<enumeration value="Unknown"></enumeration>
<enumeration value="Work"></enumeration>
<enumeration value="Home"></enumeration>
</restriction>
</simpleType>
<complexType name="Phone">
<simpleContent>
<extension base="tns:String20">
<attribute name="Category" use="optional" type="tns:PhoneCategory"></attribute>
</extension>
</simpleContent>
</complexType>
<complexType name="Customer">
<sequence>
<element name="Phone" minOccurs="1" maxOccurs="unbounded" type="tns:Phone"></element>
</sequence>
<attribute name="Name" use="required" type="tns:String20"></attribute>
<attribute name="Email" use="required">
<simpleType>
<restriction base="tns:String20">
<pattern value="[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}"></pattern>
</restriction>
</simpleType>
</attribute>
<attribute name="RegistrationDate" use="optional" type="dateTime"></attribute>
</complexType>
<element name="Customer" type="tns:Customer"></element>
</schema>

下面是合法的XML数据:

<e0:Customer Name="Tank" Email="tank@example.com" RegistrationDate="2014-04-15T16:56:53.0568964Z" xmlns:e0="http://schemas.example.com/projecta">
<e0:Phone Category="Work">12345678-334</e0:Phone>
<e0:Phone>87654321</e0:Phone>
</e0:Customer>

因为XSD相当繁琐不便于书写,MX自创了用户友好的语法来表达XML Schema的语义,下面的MX代码和上面的XSD代码表达了相同的语义:
[img]http://dl2.iteye.com/upload/attachment/0096/2115/654fdb98-5177-3ede-9c09-5b76633d6340.png[/img]

//HelloWorld.mxcs
xnamespace {"http://schemas.example.com/projecta"} [namespace: Example.ProjectA] {
type String20 restrict String
facets {
lengthrange: 1..20;
};
;
type PhoneCategory restrict String
facets{
enums: Unknown = "Unknown", Work = "Work", Home = "Home"
};
;
type Phone extend String20
attributes {
attribute Category[?] as PhoneCategory;
};
;
type Customer
attributes {
attribute Name as String20;
attribute Email as
type restrict String20
facets {
patterns: @"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}";
};
;
;
attribute RegistrationDate[?] as DateTime;
};
children {
element Phone[+; membername: Phones] as Phone;
};
;
element Customer as Customer;
}

MX编译器编译HelloWorld.mxcs后,将生成如下的C#代码:

//HelloWorld.mxcs.cs
//Generated by MX compiler
namespace Example.ProjectA
{
public partial class PhoneCategory : ...
{
public static readonly string @Unknown = "Unknown";
public static readonly string @Work = "Work";
public static readonly string @Home = "Home";
...
}
public partial class Phone : ...
{
public partial class AttributeSetClass : ...
{
public string Category_Value { get; set; }
...
}
public AttributeSetClass AttributeSet { get; set; }
public AttributeSetClass EnsureAttributeSet();
public string Value { get; set; }
...
}
public partial class Customer : ...
{
public partial class AttributeSetClass : ...
{
public string Name_Value { get; set; }
public string Email_Value { get; set; }
public DateTime? RegistrationDate_Value { get; set; }
...
}
public AttributeSetClass AttributeSet { get; set; }
public AttributeSetClass EnsureAttributeSet();
public partial class ComplexChildClass : ...
{
public partial class Phones_Class : ...
{
public partial class ItemClass : ...
{
public Phone Type { get; set; }
...
}
public ItemClass CreateAndAddItem();
...
}
public Phones_Class Phones { get; set; }
public Phones_Class Ensure_Phones();
...
}
public ComplexChildClass ComplexChild { get; set; }
public ComplexChildClass EnsureComplexChild();
...
}
public partial class Customer_ElementClass : ...
{
public Customer Type { get; set; }
public static bool TryLoadAndValidate(XmlReader reader, Metah.X.Context context, out Customer_ElementClass result);
...
}
}

使用编译器生成的代码,就可以创建、查询、修改、保存、装载及验证XML数据,下面的手写代码演示了如何使用编译器生成的代码:

//Program.cs
using System;
using System.Xml;//for XmlReader & XmlWriter
using X = Metah.X;

namespace Example.ProjectA {
class Program {
static void Main(string[] args) {
var customer = new Customer();
var cattset = customer.EnsureAttributeSet();
cattset.Name_Value = "Tank";
cattset.Email_Value = "tank@example.com";
cattset.RegistrationDate_Value = DateTime.Now;
var phones = customer.EnsureComplexChild().Ensure_Phones();
var phone = phones.CreateAndAddItem();
phone.EnsureAttributeSet().Category_Value = PhoneCategory.Work;
phone.Value = "12345678-334";
phones.CreateAndAddItem().Value = "87654321";
var customerElement = new Customer_ElementClass { Type = customer };
using (var writer = XmlWriter.Create(@"d:\customer.xml", new XmlWriterSettings { Indent = true }))
customerElement.Save(writer);
//
var ctx = new X.Context();
using (var reader = XmlReader.Create(@"d:\customer.xml")) {
Customer_ElementClass customerElement2;
if (Customer_ElementClass.TryLoadAndValidate(reader, ctx, out customerElement2)) {
var customer2 = customerElement2.Type;
Console.WriteLine("Name={0}, Email={1}, RegistrationDate={2}",
customer2.AttributeSet.Name_Value, customer2.AttributeSet.Email_Value, customer2.AttributeSet.RegistrationDate_Value);
foreach (var phone2 in customer2.ComplexChild.Phones) {
Console.WriteLine("\tCategory={0}, Value={1}", phone2.Type.AttributeSet.Category_Value, phone2.Type.Value);
}
customer2.AttributeSet.Name_Value += "-Knat";
customer2.AttributeSet.RegistrationDate = null;
var phone3 = customer2.ComplexChild.Phones.CreateAndAddItem();
phone3.EnsureAttributeSet().Category_Value = PhoneCategory.Home;
phone3.Value = "11223344";
using (var writer = XmlWriter.Create(@"d:\customer2.xml", new XmlWriterSettings { Indent = true }))
customerElement2.Save(writer);
}
else {
foreach (var diag in ctx.Diagnostics)
Console.WriteLine(diag);
}
}
}
}
}

下面是d:\customer.xml的内容:

<?xml version="1.0" encoding="utf-8"?>
<e0:Customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="e0:Customer" Name="Tank" Email="tank@example.com" RegistrationDate="2014-04-17T08:45:21.4460232Z" xmlns:e0="http://schemas.example.com/projecta">
<e0:Phone xsi:type="e0:Phone" Category="Work">12345678-334</e0:Phone>
<e0:Phone xsi:type="e0:Phone">87654321</e0:Phone>
</e0:Customer>

下面是d:\customer2.xml的内容:

<?xml version="1.0" encoding="utf-8"?>
<e0:Customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="e0:Customer" Name="Tank-Knat" Email="tank@example.com" xmlns:e0="http://schemas.example.com/projecta">
<e0:Phone xsi:type="e0:Phone" Category="Work">12345678-334</e0:Phone>
<e0:Phone xsi:type="e0:Phone">87654321</e0:Phone>
<e0:Phone xsi:type="e0:Phone" Category="Home">11223344</e0:Phone>
</e0:Customer>

也就是说,MX的用处 = XML Schema的用处 + Document Object Model的用处。欲知详情,请访问:[url]http://metah.codeplex.com/[/url]
 类似资料:

相关阅读

相关文章

相关问答