XmlSerialization和xsi:SchemaLocation(xsd.exe)

我使用xsd.exe生成一个C#类来读/写GPX文件。 如何获得生成的XML文件以包含xsi:schemaLocation属性,例如。

我想要以下但xsi:schemaLocation总是丢失

   

将其添加到生成的C#类中:

 [XmlAttribute("schemaLocation", Namespace = XmlSchema.InstanceNamespace)] public string xsiSchemaLocation = "http://www.topografix.com/GPX/1/1 " + "http://www.topografix.com/GPX/1/1/gpx.xsd"; 

显然, xsd.exe工具不会生成 schemaLocation属性。

您可以扩展类并将其添加到扩展类中,而不是修改xsd.exe生成的类以添加schemaLocation属性。

假设原始模式名为MySchema.xsd,生成的文件名为MySchema.cs,类名为MySchema。 以下是生成的类的外观:

[MySchema.cs]

 namespace MyProgram.MySchemas { using System.Xml.Serialization; ///  [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.17929")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] ... public partial class MySchema { private string someField; ... ... } } 

(请注意,该类是部分的。这意味着我们可以扩展它。)

您需要做的是创建另一个文件,在本例中我们将其称为MySchemaExtender.cs。 该文件将包含另一个具有相同类名MySchema的部分类定义:

[MySchemaExtender.cs]

 namespace MyProgram.MySchemas { using System.Xml.Serialization; public partial class MySchema { } } 

现在您需要做的就是将schemaLocation属性放在扩展类中。 以下是您的最终扩展类的外观:

[MySchemaExtender.cs]

 namespace MyProgram.MySchemas { using System.Xml.Serialization; public partial class MySchema { [XmlAttribute("schemaLocation", Namespace = System.Xml.Schema.XmlSchema.InstanceNamespace)] public string xsiSchemaLocation = @"http://someurl/myprogram http://someurl/myprogram/MySchema.xsd"; } } 

现在,如果使用xsd.exe重新生成类,则无需修改任何内容。

你必须自己做这件事。 在任何情况下,XML序列化都无法知道您希望架构的位置。

试试这个,虽然我还没有测试过:

 [XmlRoot(ElementName = "gpx", Namespace = GPX_NAMESPACE)] public class WhateverAGpxIs { private const string GPX_NAMESPACE = "http://www.topografix.com/GPX/1/1"; private const string XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance"; [XmlAttribute(AttributeName = "creator")] public string Creator = "ExpertGPS 1.1 - http://www.topografix.com"; [XmlNamespaceDeclarations] public XmlSerializerNamespaces Namespaces = new XmlSerializerNamespaces( new[] { new XmlQualifiedName("xsi", XSI_NAMESPACE), new XmlQualifiedName(string.Empty, GPX_NAMESPACE) }); [XmlAttribute(AttributeName = "schemaLocation", Namespace = XSI_NAMESPACE)] public string SchemaLocation = GPX_NAMESPACE + " " + "http://www.topografix.com/GPX/1/1/gpx.xsd"; [XmlAttribute(AttributeName = "version")] public string Version = "1.1"; } 

当然这个答案太迟了! 但也许对其他开发者有用;-)。 我使用relfection来解决这个问题,因为它必须是自动化的。

必须调用静态方法CreateMessageType。 必须是不包含schemaLocation属性的序列化类。 此方法使用as parent(名为Dynamic)返回新类型,但添加schemaLocation属性并将ElementName属性设置为XmlRootAttribute。

创建类型后,必须再次使用reflection来创建对象并设置属性。

代码在xxx中看起来很痛苦,但它就像一个魅力!

请参阅以下编码:

 /// Copying the attributes of a type to a new type private static void copyAttributes(TypeBuilder dynamictype) { try { //Iterate over all attributes of the TMessage class and copy these to the new type IList attributes = CustomAttributeData.GetCustomAttributes(typeof(TMessage)); if (attributes != null) { foreach (CustomAttributeData attribute in attributes) { List constructorarguments = new List(); if (attribute.ConstructorArguments != null) { foreach (CustomAttributeTypedArgument argument in attribute.ConstructorArguments) { constructorarguments.Add(argument.Value); } } List namedfields = new List(); List namedfieldarguments = new List(); List namedproperties = new List(); List namedpropertyarguments = new List(); if (attribute.NamedArguments != null) { //Iterate over all named arguments foreach (CustomAttributeNamedArgument argument in attribute.NamedArguments) { //Check which type of argument is found if (argument.MemberInfo is FieldInfo) { FieldInfo field = argument.MemberInfo as FieldInfo; namedfields.Add(field); namedfieldarguments.Add(argument.TypedValue.Value); } else if (argument.MemberInfo is PropertyInfo) { PropertyInfo property = argument.MemberInfo as PropertyInfo; namedproperties.Add(property); namedpropertyarguments.Add(argument.TypedValue.Value); } } } //Check if the current attribute is of type XmlRoot. //In this case the ElementName or TypeName property must also be set if (attribute.Constructor.DeclaringType.Equals(typeof(XmlRootAttribute))) { namedproperties.Add(typeof(XmlRootAttribute).GetProperty("ElementName")); namedpropertyarguments.Add(typeof(TMessage).Name); } //Build the copy of the parent attribute CustomAttributeBuilder copyattributebuilder = new CustomAttributeBuilder( attribute.Constructor, constructorarguments.ToArray(), namedproperties.ToArray(), namedpropertyarguments.ToArray(), namedfields.ToArray(), namedfieldarguments.ToArray()); //Add the attribute to the dynamic type dynamictype.SetCustomAttribute(copyattributebuilder); } } } catch (Exception exception) { throw new ApplicationException("Unable to copy attribute from parent type", exception); } } /// Create dynamic type for an operation message which includes the types for serialization /// Returns dynamic type public static Type CreateMessageType() { try { AssemblyBuilder assemblybuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(Guid.NewGuid().ToString()), AssemblyBuilderAccess.Run); ModuleBuilder modulebuilder = assemblybuilder.DefineDynamicModule(Guid.NewGuid().ToString(), false); //Create type based on an unique so that it does not conflict with the OperationMessage classname TypeBuilder typebuilder = modulebuilder.DefineType(typeof(TMessage).Name + "Dynamic", TypeAttributes.Public | TypeAttributes.Class); //Set original message type as parent of the new dynamic type typebuilder.SetParent(typeof(TMessage)); //Copy attributes from TMessage paren type to the dynamic type WMQXMLMessageTypeFactory.copyAttributes(typebuilder); //Create the xsi:schemaLocation property CustomAttributeBuilder attributebuilder = new CustomAttributeBuilder( typeof(XmlAttributeAttribute).GetConstructor(new Type[] { typeof(string) }), new object[] { "schemaLocation" }, new PropertyInfo[] { typeof(XmlAttributeAttribute).GetProperty("Namespace") }, new object[] { XmlSchema.InstanceNamespace }); FieldBuilder schemalocationfieldbuilder = typebuilder.DefineField("SchemaLocation", typeof(string), FieldAttributes.Public); schemalocationfieldbuilder.SetCustomAttribute(attributebuilder); return typebuilder.CreateType(); } catch (Exception exception) { throw new ApplicationException("Unable to create XML message type", exception); } } 

我用以下代码创建对象

 Type type = WMQXMLMessageTypeFactory.CreateMessageType(); MetaData metadata = new MetaData(); metadata.ID = Guid.NewGuid().ToString(); metadata.Created = DateTime.Now; metadata.Application = new schemasdev.local.tenant.Application(); metadata.Application.Name = "Publish Tenant"; metadata.Application.Core = ApplicationCore.PropertySystem; NewOperation newoperation = new NewOperation(); newoperation.Tenant = new Tenant(); newoperation.Tenant.Code = "001"; newoperation.Tenant.Name = "Mister X"; object request = type.GetConstructor(new Type[0]).Invoke(new object[0]); (request as TenantRequest).MetaData = metadata; (request as TenantRequest).New = newoperation; //Setting the schema location property type.InvokeMember("SchemaLocation", System.Reflection.BindingFlags.SetField, null, request, new object[] { "http://schemasdev.local/2012-01/Tenant/1.0/Tenant.xsd" }); System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(type); stream = new System.IO.MemoryStream(); serializer.Serialize(stream, request); Console.WriteLine(UTF8Encoding.UTF8.GetString(stream.ToArray())); 

最终是完美的输出:

    b59938fd-8e68-4927-87da-6d92c609f159  Publish Tenant PropertySystem  2012-02-20T10:07:54.645424+01:00    001 Mister X