使用 LINQ 解析和解释 XSD





5.00/5 (6投票s)
如何使用 Linq2Xsd 生成的对象直接操作 XmlSchema。
背景
尽管 XML 冗长、解析速度慢且数据布局模糊……它仍然是我们编码工具箱中为数不多的、公开可强制执行的数据传输机制之一。我所说的指的是 XSD 或 XML Schema。模式解释传统上是一门深奥的艺术。我写了一篇文章关于使用 MS Schema Object Model (SOM)。完成这篇文章后,我立刻意识到 SOM 存在许多缺陷,它是一个极其弱类型且笨拙的数据模型。“获取、判断类型、转换、使用”的模式一遍又一遍地重复出现,并引入了误解错误的风险。
本文是对第一次尝试的更现代、更强大的重构,使用了更通用、强类型的方法。
模式-模式
XmlSchema.xsd 定义了模式的格式……模式模式。 LinqToXsd 是一个强大的 Xsd 到 linq 友好代码生成器。XmlSchema.xsd 有点特殊,因为核心定义是通过包含更旧的文件定义语言 DTD (XMLSchema.dtd & datatypes.dtd) 来处理的。
C:\projects>linqtoxsd.exe xmlschema.xsd
[Microsoft (R) .NET Framework, Version v4.0.30319]
Generated xmlschema.cs...
请参阅源 zip 中的 XmlSchema.cs
解析
解析并不像“加载”那么简单,你需要自己组装你正在读取的源 XSD。这意味着递归地包含后续的 XSD 并构建它们的的对象映射。
/// <summary>
/// Load a schema using the include file resolver, so you can find any resource
/// </summary>
/// <param name="files"></param>
/// <param name="resolver"></param>
/// <returns></returns>
public static schema Load(IEnumerable<string> files, IncludedFileResolver resolver)
{
schema mtr = new schema();
foreach(var fil in files)
{
var sch = schema.Load(fil);
Merge(mtr, sch);
// Combine import & include
var incs = sch.import.Select(q => q.schemaLocation).ToList();
incs.AddRange(sch.include.Select(s=>s.schemaLocation));
var resolved = incs.Select(inc => resolver(fil, inc))
.Where(q=>null!=q);
if (resolved.Count() > 0)
Merge(mtr, Load(resolved));
}
return mtr;
}
存储对象
在遍历模式时,在进行过程中合并对象引用非常重要。
/// <summary> /// combines multiple schemas into one large schema object /// </summary> /// <param name="mstr"></param> /// <param name="src"></param> private static void Merge(schema mstr, schema src) { mstr.element = MergeList<element>(mstr.element, src.element); mstr.attribute = MergeList<attribute>(mstr.attribute, src.attribute); mstr.complexType = MergeList<complexType>(mstr.complexType, src.complexType); mstr.simpleType = MergeList<simpleType>(mstr.simpleType, src.simpleType); mstr.include = MergeList<include>(mstr.include, src.include); mstr.import = MergeList<import>(mstr.import, src.import); } /// <summary> /// Generic list of stuff merge mechanism. Simple ref copy if null, otherwise append /// ignores duplicate. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="dest"></param> /// <param name="src"></param> /// <returns></returns> private static IList<T> MergeList<T>(IList<T> dest, IList<T> src) { if (null == dest) return src; if (null != src && src.Count() > 0) src.Where(q=>!dest.Contains(q)).ToList().ForEach(s => dest.Add(s)); return dest; }
通过 LinqToXsd 的魔力,解析就完成了。
解释;但它意味着什么?!
XML Schema 的含义并非易事。与其尝试描述它,我将参考更好的资源
- W3C - XML Schema 开源标准
- Xml Schema Wiki
- W3C School xsd 入门 一个好的起点
做一些有用的事情
我们已经加载了所有对象,映射了它们,找到了它们的用途,现在让我们将它们重新解释成不同的东西,但仍然有意义。在这种情况下,我创建了一种名为“SKA”(来自 SKemA)的“新”语言。它只是对 Xsd 的更自然的类 C 语言重新解释,用于演示。
一些基本的英文规则
- 单独一行上的符号是根元素。
- "Type [name] {" 是一个复杂类型定义。Choice、Enum 等也是如此...
- 简单类型被扩展到它们的组成基本元素。
- 元素默认类型为与名称相同 + "Info" 的复杂类型。
//
// Ska(c) 2011, Bruce Meacham - An intuitive Xml Schema language
// DO NOT EDIT - This is file was generated on 5/4/2012 6:51:18 AM by ska.exe
//
Type CubInfo {
!Id
!First
!Last
@Place
}
Type GroupInfo {
Cub [0-n]
@Name
}
Type RacersInfo {
Group [0-n]
}
Type ResultInfo {
!CubId
!Time
}
Type RaceInfo {
Result [0-n]
}
Type RacesInfo {
Race [0-n]
}
Type DerbyInfo {
Racers
Races
}
Derby
该语言牺牲了可读性和简洁性,以换取自定义的 xml 格式和正式 XmlSchema 标准的稳健性。尝试加载更复杂的示例。 IRS 1040 MeF Schema 是我的核心测试模式,用 Ska 查看它会很有趣。
如果您运行提供的示例程序,它将产生此输出。
摘要
就像我之前关于模式的文章一样,这仅仅是许多 XSD 相关功能的起点。代码生成器、翻译器或验证器。