65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 LINQ 解析和解释 XSD

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2012年5月6日

CPOL

2分钟阅读

viewsIcon

43826

downloadIcon

1048

如何使用 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 的含义并非易事。与其尝试描述它,我将参考更好的资源

做一些有用的事情

我们已经加载了所有对象,映射了它们,找到了它们的用途,现在让我们将它们重新解释成不同的东西,但仍然有意义。在这种情况下,我创建了一种名为“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 相关功能的起点。代码生成器、翻译器或验证器。

© . All rights reserved.