XmlSerializer : 불필요한 xsi 및 xsd 네임 스페이스 제거


132

루트 요소에 기본 네임 스페이스를 쓰지 않도록 XmlSerializer를 구성하는 방법이 있습니까?

내가 얻는 것은 이것입니다 :

<?xml ...>
<rootelement xmlns:xsi="..." xmlns:xsd="...">
</rootelement>

xmlns 선언을 모두 제거하고 싶습니다.

중복 : xmlns =”…”를 얻지 않고 객체를 XML로 직렬화하는 방법은 무엇입니까?

답변:


63

Dave가 .NET 에서 객체를 직렬화 할 때 모든 xsi 및 xsd 네임 스페이스 생략에 대한 답변을 반복하도록 요청 했으므로이 게시물을 업데이트하고 위에서 언급 한 링크에서 답변을 반복했습니다. 이 답변에 사용 된 예는 다른 질문에 사용 된 예와 동일합니다. 다음 내용이 그대로 복사됩니다.


Microsoft의 설명서와 여러 가지 솔루션을 온라인에서 읽은 후이 문제에 대한 해결책을 찾았습니다. 를 통해 내장 XmlSerializer및 사용자 정의 XML 직렬화와 함께 작동합니다 IXmlSerialiazble.

간단히 말해서, 지금 MyTypeWithNamespaces까지이 질문에 대한 답변에 사용 된 것과 동일한 XML 샘플을 사용하겠습니다.

[XmlRoot("MyTypeWithNamespaces", Namespace="urn:Abracadabra", IsNullable=false)]
public class MyTypeWithNamespaces
{
    // As noted below, per Microsoft's documentation, if the class exposes a public
    // member of type XmlSerializerNamespaces decorated with the 
    // XmlNamespacesDeclarationAttribute, then the XmlSerializer will utilize those
    // namespaces during serialization.
    public MyTypeWithNamespaces( )
    {
        this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
            // Don't do this!! Microsoft's documentation explicitly says it's not supported.
            // It doesn't throw any exceptions, but in my testing, it didn't always work.

            // new XmlQualifiedName(string.Empty, string.Empty),  // And don't do this:
            // new XmlQualifiedName("", "")

            // DO THIS:
            new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
            // Add any other namespaces, with prefixes, here.
        });
    }

    // If you have other constructors, make sure to call the default constructor.
    public MyTypeWithNamespaces(string label, int epoch) : this( )
    {
        this._label = label;
        this._epoch = epoch;
    }

    // An element with a declared namespace different than the namespace
    // of the enclosing type.
    [XmlElement(Namespace="urn:Whoohoo")]
    public string Label
    {
        get { return this._label; }
        set { this._label = value; }
    }
    private string _label;

    // An element whose tag will be the same name as the property name.
    // Also, this element will inherit the namespace of the enclosing type.
    public int Epoch
    {
        get { return this._epoch; }
        set { this._epoch = value; }
    }
    private int _epoch;

    // Per Microsoft's documentation, you can add some public member that
    // returns a XmlSerializerNamespaces object. They use a public field,
    // but that's sloppy. So I'll use a private backed-field with a public
    // getter property. Also, per the documentation, for this to work with
    // the XmlSerializer, decorate it with the XmlNamespaceDeclarations
    // attribute.
    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces Namespaces
    {
        get { return this._namespaces; }
    }
    private XmlSerializerNamespaces _namespaces;
}

이것이이 수업의 전부입니다. 이제 일부 XmlSerializerNamespaces는 자신의 클래스 내에 객체 를 두는 것에 반대했습니다 . 그러나 당신이 볼 수 있듯이, 나는 기본 생성자에 깔끔하게 자리 잡고 네임 스페이스를 반환하는 공용 속성을 노출했습니다.

이제 클래스를 직렬화 할 때 다음 코드를 사용합니다.

MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42);

/******
   OK, I just figured I could do this to make the code shorter, so I commented out the
   below and replaced it with what follows:

// You have to use this constructor in order for the root element to have the right namespaces.
// If you need to do custom serialization of inner objects, you can use a shortened constructor.
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces), new XmlAttributeOverrides(),
    new Type[]{}, new XmlRootAttribute("MyTypeWithNamespaces"), "urn:Abracadabra");

******/
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
    new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });

// I'll use a MemoryStream as my backing store.
MemoryStream ms = new MemoryStream();

// This is extra! If you want to change the settings for the XmlSerializer, you have to create
// a separate XmlWriterSettings object and use the XmlTextWriter.Create(...) factory method.
// So, in this case, I want to omit the XML declaration.
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Encoding = Encoding.UTF8; // This is probably the default
// You could use the XmlWriterSetting to set indenting and new line options, but the
// XmlTextWriter class has a much easier method to accomplish that.

// The factory method returns a XmlWriter, not a XmlTextWriter, so cast it.
XmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws);
// Then we can set our indenting options (this is, of course, optional).
xtw.Formatting = Formatting.Indented;

// Now serialize our object.
xs.Serialize(xtw, myType, myType.Namespaces);

이 작업을 완료하면 다음과 같은 결과가 나타납니다.

<MyTypeWithNamespaces>
    <Label xmlns="urn:Whoohoo">myLabel</Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

웹 서비스 호출을 위해 XML로 직렬화되는 클래스가 심층적 인 최근 프로젝트 에서이 방법을 성공적으로 사용했습니다. Microsoft의 문서는 공개적으로 접근 가능한 방법으로 수행 할 작업에 대해 명확하지 않습니다.XmlSerializerNamespaces 일단 회원을 만든 후에 , 많은 사람들이 쓸모 없다고 생각합니다. 그러나 설명서를 따라 위에서 설명한 방식으로 사용하면 지원되지 않는 동작이나 "자신의 롤링"직렬화를 구현하지 않고 XmlSerializer가 클래스에 대해 XML을 생성하는 방법을 사용자 지정할 수 있습니다 IXmlSerializable.

이 답변이 모든 표준에 의해 생성 된 표준 xsixsd네임 스페이스를 제거하는 방법을 한 번에 쉬게하기를 희망합니다 .XmlSerializer .

업데이트 : 모든 네임 스페이스를 제거하는 것에 대한 OP의 질문에 대답하고 싶습니다. 위의 코드는이 작업을 수행합니다. 방법을 보여 드리겠습니다. 위의 예에서 사용중인 네임 스페이스가 두 개이므로 모든 네임 스페이스를 제거 할 수 없습니다. XML 문서 어딘가에와 같은 것이 필요합니다 xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo. 예제에서 클래스가 큰 문서의 일부인 경우, 어딘가 네임 스페이스 위 중의 하나 (또는 둘 다)에 대해 선언해야 Abracadbra하고 Whoohoo. 그렇지 않은 경우 네임 스페이스 중 하나 또는 둘 다에있는 요소를 일종의 접두어로 장식해야합니다 (두 개의 기본 네임 스페이스를 가질 수 없습니까?). 따라서이 예에서는 Abracadabra기본 네임 스페이스입니다. MyTypeWithNamespaces클래스 내 에서 네임 스페이스의 네임 스페이스 접두사를 다음 Whoohoo과 같이 추가 할 수 있습니다 .

public MyTypeWithNamespaces
{
    this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
        new XmlQualifiedName(string.Empty, "urn:Abracadabra"), // Default Namespace
        new XmlQualifiedName("w", "urn:Whoohoo")
    });
}

이제 클래스 정의에서 <Label/>요소가 namespace에 있음을 나타내 "urn:Whoohoo"므로 더 이상 아무것도 할 필요가 없습니다. 위의 직렬화 코드를 변경하지 않고 클래스를 직렬화하면 다음과 같이 출력됩니다.

<MyTypeWithNamespaces xmlns:w="urn:Whoohoo">
    <w:Label>myLabel</w:Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

<Label>문서의 나머지 부분과 다른 네임 스페이스에 있기 때문에 네임 스페이스를 사용하여 "장식"해야합니다. 아무 여전히 있다는 것을 공지 사항 xsixsd네임 스페이스.


이것으로 다른 질문에 대한 대답이 끝납니다. 그러나 네임 스페이스를 사용하지 않는 것에 대한 OP의 질문에 아직 답변하지 않았다고 생각하기 때문에 OP를 원했습니다. <Label>이것이 문서의 나머지 부분과 동일한 네임 스페이스의 일부 라고 가정하십시오 urn:Abracadabra.

<MyTypeWithNamespaces>
    <Label>myLabel<Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

생성자는 기본 코드 이름을 검색하기 위해 public 속성과 함께 첫 번째 코드 예제에서와 같이 보입니다.

// As noted below, per Microsoft's documentation, if the class exposes a public
// member of type XmlSerializerNamespaces decorated with the 
// XmlNamespacesDeclarationAttribute, then the XmlSerializer will utilize those
// namespaces during serialization.
public MyTypeWithNamespaces( )
{
    this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
        new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
    });
}

[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Namespaces
{
    get { return this._namespaces; }
}
private XmlSerializerNamespaces _namespaces;

그런 다음 나중에 MyTypeWithNamespaces객체를 사용하여 직렬화하는 코드에서 위와 같이 호출합니다.

MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42);

XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
    new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });

...

// Above, you'd setup your XmlTextWriter.

// Now serialize our object.
xs.Serialize(xtw, myType, myType.Namespaces);

그리고 XmlSerializer출력에 추가 네임 스페이스가없는 바로 위에 표시된 것과 동일한 XML을 뱉어냅니다.

<MyTypeWithNamespaces>
    <Label>myLabel<Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

완전성을 기하기 위해 단순히 대답하는 대신 여기에 정답을 포함시켜야하며, '지원되지 않는 행동'이라는 결론을 내리는 방법에 관심이 있습니다.
Dave Van den Eynde

1
내가 찾은 가장 간단한 설명이므로 다시 확인했습니다. 감사합니다 @fourpastmidnight
Andre Albuquerque

2
나는 그것을 얻지 못한다. 최종 OP의 답변을 위해 직렬화 "urn : Abracadabra"(생성자) 중에 네임 스페이스를 계속 사용하고 있는데 왜 최종 출력에 포함되지 않습니까? OP 사용하지 않아야합니다. XmlSerializerNamespaces EmptyXmlSerializerNamespaces = new XmlSerializerNamespaces (new [] {XmlQualifiedName.Empty});
dparkar

2
가장 많이 투표 된 것은 아니지만 정답입니다. 나를 위해 작동하지 않은 기발한 것은 XmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws);로 대체해야했습니다 var xtw = XmlTextWriter.Create(memStm, xws);.
Leonel Sanches da Silva

1
이 답변을 쓴 지 오래되었습니다. XmlTextWriter.Create(추상?) XmlWriter인스턴스를 반환 합니다. 따라서 @ Preza8이 정확하므로 다른 XmlTextWriter특정 속성을 (다운 캐스팅하지 않고는) 설정 할 수 없으므로 특정 캐스트는로 캐스팅됩니다 XmlTextWriter.
fourpastmidnight

257
//Create our own namespaces for the output
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();

//Add an empty namespace and empty value
ns.Add("", "");

//Create the serializer
XmlSerializer slz = new XmlSerializer(someType);

//Serialize the object with our own namespaces (notice the overload)
slz.Serialize(myXmlTextWriter, someObject, ns)

24
흠 ... 너는 반역자 야. msdn.microsoft.com/en-us/library/…에 명시 적으로 나와 있습니다.
랄프 라벨

부야! (ms가 말한 것을 극복하기 위해 할 수없는 일)
granadaCoder

3
왜 이것이 "지원되지 않는"지 잘 모르겠지만, 이것이 내가 원하는 것을 정확하게 수행합니다.
Dan Bechard

8
이 답변은 "xmlns : d1p1"및 "xmlns : q1"네임 스페이스를 생성합니다. 이게 뭐야?
Leonel Sanches 다 실바

2
이 코드는 다른 네임 스페이스 정의없이 실제로 매우 간단한 직렬화에 작동합니다. 여러 네임 스페이스 정의의 경우 작업 답변이 허용됩니다.
Leonel Sanches 다 실바

6

대안이 있습니다 . 직렬화 할 형식에 XmlSerializerNamespaces 형식의 멤버를 제공 할 수 있습니다 . XmlNamespaceDeclarations로 장식하십시오. 속성으로 . 네임 스페이스 접두사와 URI를 해당 멤버에 추가하십시오. 그런 다음 XmlSerializerNamespaces를 명시 적으로 제공하지 않는 직렬화는 사용자가 입력 한 네임 스페이스 접두사 + URI 쌍을 사용합니다.

예제 코드, 이것이 당신의 유형이라고 가정하십시오 :

[XmlRoot(Namespace = "urn:mycompany.2009")]
public class Person {
  [XmlAttribute] 
  public bool Known;
  [XmlElement]
  public string Name;
  [XmlNamespaceDeclarations]
  public XmlSerializerNamespaces xmlns;
}

당신은 이것을 할 수 있습니다 :

var p = new Person
  { 
      Name = "Charley",
      Known = false, 
      xmlns = new XmlSerializerNamespaces()
  }
p.xmlns.Add("",""); // default namespace is emoty
p.xmlns.Add("c", "urn:mycompany.2009");

즉, 고유 한 접두사 + URI 쌍을 지정하지 않은 인스턴스의 직렬화는 "urn : mycompany.2009"네임 스페이스에 "p"접두사를 사용합니다. 또한 xsi 및 xsd 네임 스페이스도 생략합니다.

차이점은 XmlSerializer.Serialize ()를 호출 할 때 XmlSerializerNamespaces를 명시 적으로 사용하지 않고 형식 자체에 추가한다는 것입니다. 즉, 소유하지 않은 코드 (예 : 웹 서비스 스택)로 유형의 인스턴스가 직렬화되고 해당 코드가 XmlSerializerNamespaces를 명시 적으로 제공하지 않으면 해당 직렬 변환기는 인스턴스에 제공된 네임 스페이스를 사용합니다.


1. 차이가 보이지 않습니다. 여전히 XmlSerializerNamespaces 인스턴스에 기본 네임 스페이스를 추가하고 있습니다.
Dave Van den Eynde

3
2. 이것은 클래스를 더 오염시킵니다. 내 목표는 특정 네임 스페이스를 사용하지 않고 내 목표는 네임 스페이스를 전혀 사용하지 않는 것입니다.
Dave Van den Eynde

이 방법과 직렬화 중 XmlSerializerNamespaces 지정의 차이점에 대한 메모를 추가했습니다.
Cheeso

0

나는 사용하고있다 :

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        const string DEFAULT_NAMESPACE = "http://www.something.org/schema";
        var serializer = new XmlSerializer(typeof(Person), DEFAULT_NAMESPACE);
        var namespaces = new XmlSerializerNamespaces();
        namespaces.Add("", DEFAULT_NAMESPACE);

        using (var stream = new MemoryStream())
        {
            var someone = new Person
            {
                FirstName = "Donald",
                LastName = "Duck"
            };
            serializer.Serialize(stream, someone, namespaces);
            stream.Position = 0;
            using (var reader = new StreamReader(stream))
            {
                Console.WriteLine(reader.ReadToEnd());
            }
        }
    }
}

다음 XML을 얻으려면

<?xml version="1.0"?>
<Person xmlns="http://www.something.org/schema">
  <FirstName>Donald</FirstName>
  <LastName>Duck</LastName>
</Person>

네임 스페이스를 원하지 않으면 DEFAULT_NAMESPACE를 ""로 설정하십시오.


이 질문은 10 년이 넘었지만 그 당시의 요점은 네임 스페이스 선언이 전혀 포함되지 않은 XML 본문을 갖는 것이 었습니다.
Dave Van den Eynde

1
10 년이 지난 질문에 내 자신의 답을 추가하면, 정식 판에서 받아 들여지는 답이 성서보다 더 길기 때문입니다.
Maxence

그리고 가장 많이 투표 된 답변은 권장되지 않는 접근 방식 (빈 네임 스페이스)을 권장합니다.
막스

나는 그것을 도울 수 없습니다. 허용 된 답변을 가장 정확한 것으로 생각할 수 있습니다.
Dave Van den Eynde
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.