根据 XSD 兼容 XML 输入 -> XSD 兼容 XML 输出的 1:1 映射从 XSD 生成 XSLT 文件
Posted
技术标签:
【中文标题】根据 XSD 兼容 XML 输入 -> XSD 兼容 XML 输出的 1:1 映射从 XSD 生成 XSLT 文件【英文标题】:Generating an XSLT file from XSD based upon a 1:1 mapping XSD compliant XML in -> XSD compliant XML out 【发布时间】:2020-04-27 16:53:48 【问题描述】:我知道这个问题的开头是有争议的,所以我希望我已经添加了足够的说明,并且人们阅读了这些。
我有一个相对复杂的 XSD 文件(以及一组 XSD 文件,用于每个版本的架构)。 我最终要寻找的是一组 XSLT 文件,它们可以采用 XSD v12 兼容的 XML 文件,并将其转换(丢弃很多东西),直到它成为 XSD v3 兼容的 XML 文件。一些转换可能会更智能一些,例如采用 gradientStartColor 并将其分配给 backgroundFillColor 如果 gradientMode="3"... 但我不希望这部分自动完成。
所以我的第 1 步是: 生成一个“匹配”一个 XSD 文件的 XSLT 文件,这样一个符合模式的 XML 文件就可以原封不动地通过。但是,架构不兼容的 XML 文件会删除所有这些不兼容的属性/元素。我什至不会关心值的验证。
我原以为会有一种方法可以自动生成这样的 XSLT 文件。但是我的谷歌搜索没有结果。
我知道 XSLT 本身并不是模式感知的(至少在 XSLT 1 中),但我希望 XSLT 模板的某些自动生成可以通过 XSD 进行枚举,以添加足够的“锚定”来模拟模式. 还是我被这个想法误导了?
谢谢
【问题讨论】:
您是要完成工作还是要进行研究计划?如果是前者,请卷起袖子,亲手编写 XSLT(或聘请 XSLT 专家);如果是后者,并且您希望有一种简单的自动生成此类 XSLT 文件的方法,那么您就大大低估了复杂性。无论哪种方式,您手头上的内容远不止 SO Q/A。祝您好运,如果您在通用解决方案上取得进展,请与我们联系。 【参考方案1】:这是一件很难尝试的事情,没有人做过这件事我并不感到惊讶。人们已经针对问题的一小部分撰写了博士论文,例如采用两种语法(内容模型)并确定一个是否是另一个的子集。
很明显,有些映射是无法自动化的:虽然删除不再允许的元素很容易,但添加已成为强制性的元素,或重命名已更改名称的元素,或更新已更改类型的元素,正在更难。
如果您将雄心壮志限制在几个简单的案例上,您或许能够取得进展。但我会专注于使其对您的特定用例有用,而不是使其通用。
【讨论】:
我猜我期待可能会有一些东西可以为每个 xsd 复杂类型生成一个 xsl:template,每个属性都有适当的映射,并为每个子元素应用模板。 . 我想将 XML 模式转换成 XmlSerializer 类结构会更容易(使用 CodeDom),将其编译成程序集,反序列化为 XSD 生成的类,然后将其重新序列化回新的 XML 文件.感觉 XSLT 应该让它变得更容易。 如果有人在您之前编写了您需要的程序,那总是很好,但遗憾的是,这并不总是发生。【参考方案2】:好的,所以解决方案是一些 .NET 代码。
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using Microsoft.CSharp;
namespace Reverter
class Program
static int Main(string[] args)
//try
if (args.Length < 2)
Console.Error.WriteLine("Reverter schema.xsd inputfile1 inputfile2...");
return 1;
else
var schema = args[0];
List<string> srcFiles = new List<string>(args);
srcFiles.RemoveAt(0); // we get rid of the first entry, the schema
XmlSchemas xsds = new XmlSchemas();
XmlSchema xsd;
using (var r = File.OpenText(schema))
xsd = XmlSchema.Read(r, null);
xsds.Add(xsd);
xsds.Compile(null, true);
XmlSchemaImporter schemaImporter = new XmlSchemaImporter(xsds);
// create the codedom
CodeNamespace codeNamespace = new CodeNamespace("Schema");
XmlCodeExporter codeExporter = new XmlCodeExporter(codeNamespace);
List<XmlTypeMapping> maps = new List<XmlTypeMapping>();
foreach (XmlSchemaElement schemaElement in xsd.Elements.Values)
maps.Add(schemaImporter.ImportTypeMapping(schemaElement.QualifiedName));
foreach (XmlSchemaType schemaElement in xsd.Items.OfType<XmlSchemaType>())
maps.Add(schemaImporter.ImportSchemaType(schemaElement.QualifiedName));
foreach (XmlTypeMapping map in maps)
codeExporter.ExportTypeMapping(map);
codeNamespace.Types.OfType<CodeTypeDeclaration>().First(x => x.Name == "ROOTELEMENTNAME").Members.Add(
new CodeMemberProperty()
Name = "xsiSchemaLocation",
Attributes = MemberAttributes.Public | MemberAttributes.Final,
CustomAttributes =
new CodeAttributeDeclaration("System.Xml.Serialization.XmlAttribute",
new CodeAttributeArgument[]
new CodeAttributeArgument(new CodePrimitiveExpression("noNamespaceSchemaLocation")),
new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(XmlSchema.InstanceNamespace)),
)
,
Type = new CodeTypeReference(typeof(string)),
HasGet = true,
GetStatements =
new CodeMethodReturnStatement(new CodePrimitiveExpression(schema))
,
HasSet = true,
);
// Check for invalid characters in identifiers
CodeGenerator.ValidateIdentifiers(codeNamespace);
CodeCompileUnit ccu = new CodeCompileUnit();
ccu.Namespaces.Add(codeNamespace);
CompilerParameters comParams = new CompilerParameters(
new string[] "System.dll", "System.Xml.dll" );
comParams.GenerateInMemory = true;
comParams.CompilerOptions = "/optimize";
CodeGeneratorOptions codeOptions = new CodeGeneratorOptions();
codeOptions.VerbatimOrder = true;
TextWriter memText = new StringWriter();
// output the C# code
CodeDomProvider codeProvider = new CSharpCodeProvider();
var codeResult = codeProvider.CompileAssemblyFromDom(comParams, new CodeCompileUnit[] ccu );
XmlSerializer ser = new XmlSerializer(codeResult.CompiledAssembly.GetType("Schema.ROOTELEMENTTYPE", true, true));
Object obj;
XmlWriterSettings xmlSettings = new XmlWriterSettings();
xmlSettings.Indent = true;
xmlSettings.Encoding = System.Text.Encoding.UTF8;
xmlSettings.OmitXmlDeclaration = false;
foreach (string srcFile in srcFiles)
var dstFile = "New" + srcFile;
// using our XmlSerializer, we will load and then save the XMLfile
using (var file = new XmlTextReader(srcFile))
using(var outFile = XmlWriter.Create(dstFile, xmlSettings))
obj = ser.Deserialize(file);
ser.Serialize(outFile, obj);
/*catch (Exception ex)
Console.Error.WriteLine("Revert code generation failed.");
Console.Error.Write(ex.ToString());
return 2;
*/
return 0;
几乎只需将 XSD 文件作为 CodeDom 引擎的输入,生成编译的程序集,从新程序集中获取根类型,然后反序列化并重新序列化对象。如果您想要一些控制台打印被丢弃的内容,那么您可以为 XmlDeserializer 上的 UnknownElement、UnknownAttribute 或 UnknownNode 事件生成回调。
【讨论】:
以上是关于根据 XSD 兼容 XML 输入 -> XSD 兼容 XML 输出的 1:1 映射从 XSD 生成 XSLT 文件的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 System.Xml.Schema 从 xs:choice 解析 xs:annotation