【问题标题】:XmlSerialization and xsi:SchemaLocation (xsd.exe)XmlSerialization 和 xsi:SchemaLocation (xsd.exe)
【发布时间】:2010-11-27 08:35:21
【问题描述】:

我使用 xsd.exe 生成了一个用于读取/写入 GPX 文件的 C# 类。如何让生成的 XML 文件包含 xsi:schemaLocation 属性 例如。

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

<?xml version="1.0"?>
<gpx 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    version="1.1" 
    xmlns="http://www.topografix.com/GPX/1/1"
    creator="ExpertGPS 1.1 - http://www.topografix.com"
    xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
</gpx>

【问题讨论】:

    标签: c# xsd xml-serialization xsd.exe gpx


    【解决方案1】:

    将此添加到您生成的 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 工具does not generate 具有schemaLocation 属性。

    【讨论】:

    • 它将 schemaLocation 设置为什么? XSD.EXE 使用它的位置不太可能在 Web 上可用,这是xsi:schemaLocation 的用户需要找到它的位置。
    • @John:也许有一些选项可以指定 xsd 文件中的值?
    • 一个好主意是将它放在一个额外的文件中并使用部分类来添加成员,这样您就可以在不丢失 xsiSchemaLocation 的情况下生成新类。
    • 这段代码在 C# 类中的什么位置?你能在上下文中展示它吗?谢谢。
    【解决方案2】:

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

    假设原始架构名为 MySchema.xsd,生成的文件名为 MySchema.cs,类名为 MySchema。生成的类可能如下所示:

    [MySchema.cs]

    namespace MyProgram.MySchemas {
        using System.Xml.Serialization;
    
    
        /// <remarks/>
        [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 重新生成类,则无需修改任何内容。

    【讨论】:

    • 完全正确——这应该是公认的答案。如果您使用 StyleCop 之类的工具,该方法可能会给您一个警告,但您可以轻松解决这个问题,例如添加您自己的虚假 评论标签。
    【解决方案3】:

    您必须自己完成此操作。在任何情况下,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";
    }
    

    【讨论】:

      【解决方案4】:

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

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

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

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

      见下面的编码:

      /// <summary>Copying the attributes of a type to a new type</summary>
      private static void copyAttributes<TMessage>(TypeBuilder dynamictype)
      {
          try
          {
              //Iterate over all attributes of the TMessage class and copy these to the new type
              IList<CustomAttributeData> attributes = CustomAttributeData.GetCustomAttributes(typeof(TMessage));
              if (attributes != null)
              {
                foreach (CustomAttributeData attribute in attributes)
                {
                    List<object> constructorarguments = new List<object>();
                    if (attribute.ConstructorArguments != null)
                    {
                        foreach (CustomAttributeTypedArgument argument in attribute.ConstructorArguments)
                        {
                            constructorarguments.Add(argument.Value);
                        }
                    }
      
                    List<FieldInfo> namedfields = new List<FieldInfo>();
                    List<object> namedfieldarguments = new List<object>();
      
                    List<PropertyInfo> namedproperties = new List<PropertyInfo>();
                    List<object> namedpropertyarguments = new List<object>();
      
                    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);
        }
      }
      
      /// <summary>Create dynamic type for an operation message which includes the types for serialization</summary>
      /// <returns>Returns dynamic type</returns>
      public static Type CreateMessageType<TMessage>()
      {
          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<TMessage>(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<TenantRequest>();
      
      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()));
      

      最终完美的输出:

      <?xml version="1.0"?>
      <TenantRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xmlns:xsd="http://www.w3.org/2001/XMLSchema"   xsi:schemaLocation="http://schemasdev.local/2012-01/Tenant/1.0/Tenant.xsd" xmlns="http://schemasdev.local/2012-01/Tenant/1.0">
          <MetaData xmlns="http://schemasdev.local/2012-01/Messaging/1.0">
              <ID>b59938fd-8e68-4927-87da-6d92c609f159</ID>
              <Application>
                  <Name>Publish Tenant</Name>
                  <Core>PropertySystem</Core>
              </Application>
              <Created>2012-02-20T10:07:54.645424+01:00</Created>
          </MetaData>
          <New>
              <Tenant>
                  <Code>001</Code>
                  <Name>Mister X</Name>
              </Tenant>
          </New>
      </TenantRequest>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-09-05
        • 1970-01-01
        • 2011-11-28
        • 1970-01-01
        • 2011-07-24
        • 2011-08-18
        相关资源
        最近更新 更多