2009-09-10 4 views
12

xsd.exe를 사용하여 GPX 파일을 읽고 쓰는 C# 클래스를 생성했습니다. 결과 XML 파일에 xsi : schemaLocation 속성 을 포함 시키려면 어떻게해야합니까? 의 schemaLocation 항상XmlSerialization 및 xsi : SchemaLocation (xsd.exe)

<?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> 

답변

33

가 생성 된 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 generateschemaLocation 속성

나는 다음과 같은 있지만, XSI를 원한다.

+0

감사합니다. –

+0

schemaLocation을 어떻게 설정합니까? XSD.EXE에서 사용 된 위치는 웹에서 사용 가능하지 않으므로 xsi : schemaLocation 사용자가 찾아야합니다. –

+0

@ John : 아마도 xsd 파일의 값을 지정하는 옵션이 있습니까? – dtb

2

독자적으로해야합니다. 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"; 
} 
2

이 답변이 너무 늦었입니다! 그러나 다른 개발자에게 유용 할 수도 있습니다 ;-). 이 문제를 해결하기 위해 자동화를해야했기 때문에 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> 
3

대신 클래스를 수정

아래의 코드를 참조하십시오 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; 

     ... 
     ... 
    } 
} 

당신이 필요 무엇 do는 다른 파일을 만드는 것이며,이 예제에서는 MySchemaExtender.cs라고 부를 것이다. 이 파일에는 MySchema라는 동일한 클래스 이름을 가진 다른 부분 클래스 정의가 포함됩니다.

[MySchemaExtender.cs]

namespace MyProgram.MySchemas { 
    using System.Xml.Serialization; 

    public partial class MySchema {   
    } 
} 

이제 확장 된 클래스에 schemaLocation 속성을 넣으면됩니다. 당신은 당신이 무엇을 수정할 필요가 없습니다 xsd.exe 사용하여 클래스를 다시 생성하면 이제

[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"; 
    } 
} 

: 여기에 최종 확장 한 클래스이 다음과 같이 표시됩니다.

+0

정확히 - 이것은 허용 된 대답이어야합니다. 이 방법을 사용하면 StyleCop과 같은 도구를 사용하면 경고 메시지가 표시 될 수 있지만 가짜 주석 태그를 추가하여 쉽게 해결할 수 있습니다. –