카테고리 보관물: C#

C#

.NET에서 객체를 직렬화 할 때 모든 xsi 및 xsd 네임 스페이스를 생략 하시겠습니까? 네임 스페이스가 포함됩니다. <message xmlns:xsi=\”http://www.w3.org/2001/XMLSchema-instance\”

코드는 다음과 같습니다.

StringBuilder builder = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
using (XmlWriter xmlWriter = XmlWriter.Create(builder, settings))
{
    XmlSerializer s = new XmlSerializer(objectToSerialize.GetType());
    s.Serialize(xmlWriter, objectToSerialize);
}

결과 직렬화 된 문서에는 다음과 같은 네임 스페이스가 포함됩니다.

<message xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 
    xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" 
    xmlns="urn:something">
 ...
</message>

xsi 및 xsd 네임 스페이스를 제거하려면 xmlns =”…”를 얻지 않고 객체를 XML로 직렬화하는 방법 의 답변을 따를 수 있습니다 . .

메시지 태그를 <message>네임 스페이스 속성없이 사용 하고 싶습니다 . 어떻게해야합니까?



답변

...
XmlSerializer s = new XmlSerializer(objectToSerialize.GetType());
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("","");
s.Serialize(xmlWriter, objectToSerialize, ns);


답변

이것은 두 가지 답변 중 두 번째입니다.

직렬화 중에 문서에서 모든 네임 스페이스를 임의로 제거하려는 경우 고유 한 XmlWriter를 구현하여이를 수행 할 수 있습니다.

가장 쉬운 방법은 XmlTextWriter에서 파생하여 네임 스페이스를 내보내는 StartElement 메서드를 재정의하는 것입니다. StartElement 메서드는 루트를 포함한 모든 요소를 ​​방출 할 때 XmlSerializer에 의해 호출됩니다. 각 요소의 네임 스페이스를 재정의하고 빈 문자열로 바꾸면 출력에서 ​​네임 스페이스를 제거했습니다.

public class NoNamespaceXmlWriter : XmlTextWriter
{
    //Provide as many contructors as you need
    public NoNamespaceXmlWriter(System.IO.TextWriter output)
        : base(output) { Formatting= System.Xml.Formatting.Indented;}

    public override void WriteStartDocument () { }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        base.WriteStartElement("", localName, "");
    }
}

이것이 유형이라고 가정하십시오.

// explicitly specify a namespace for this type,
// to be used during XML serialization.
[XmlRoot(Namespace="urn:Abracadabra")]
public class MyTypeWithNamespaces
{
    // private fields backing the properties
    private int _Epoch;
    private string _Label;

    // explicitly define a distinct namespace for this element
    [XmlElement(Namespace="urn:Whoohoo")]
    public string Label
    {
        set {  _Label= value; }
        get { return _Label; }
    }

    // this property will be implicitly serialized to XML using the
    // member name for the element name, and inheriting the namespace from
    // the type.
    public int Epoch
    {
        set {  _Epoch= value; }
        get { return _Epoch; }
    }
}

직렬화 중에 이러한 것을 사용하는 방법은 다음과 같습니다.

        var o2= new MyTypeWithNamespaces { ..intializers.. };
        var builder = new System.Text.StringBuilder();
        using ( XmlWriter writer = new NoNamespaceXmlWriter(new System.IO.StringWriter(builder)))
        {
            s2.Serialize(writer, o2, ns2);
        }
        Console.WriteLine("{0}",builder.ToString());

XmlTextWriter는 다소 고장났습니다. 참조 doc 에 따르면 쓸 때 다음을 확인하지 않습니다.

  • 속성 및 요소 이름에 유효하지 않은 문자가 있습니다.

  • 지정된 인코딩에 맞지 않는 유니 코드 문자 유니 코드 문자가 지정된 인코딩에 맞지 않으면 XmlTextWriter는 유니 코드 문자를 문자 엔터티로 이스케이프하지 않습니다.

  • 속성이 중복되었습니다.

  • DOCTYPE 공용 식별자 또는 시스템 식별자의 문자

XmlTextWriter에 대한 이러한 문제는 .NET Framework v1.1 이후로 발생했으며 이전 버전과의 호환성을 위해 계속 남아 있습니다. 이러한 문제에 대해 걱정이 없다면 반드시 XmlTextWriter를 사용하십시오. 그러나 대부분의 사람들은 조금 더 신뢰성을 원합니다.

그것을 얻으려면 XmlTextWriter에서 파생하는 대신 직렬화 중에 네임 스페이스를 계속 억제하면서 추상 XmlWriter 및 24 메소드 의 구체적인 구현을 정의하십시오 .

예를 들면 다음과 같습니다.

public class XmlWriterWrapper : XmlWriter
{
    protected XmlWriter writer;

    public XmlWriterWrapper(XmlWriter baseWriter)
    {
        this.Writer = baseWriter;
    }

    public override void Close()
    {
        this.writer.Close();
    }

    protected override void Dispose(bool disposing)
    {
        ((IDisposable) this.writer).Dispose();
    }

    public override void Flush()
    {
        this.writer.Flush();
    }

    public override string LookupPrefix(string ns)
    {
        return this.writer.LookupPrefix(ns);
    }

    public override void WriteBase64(byte[] buffer, int index, int count)
    {
        this.writer.WriteBase64(buffer, index, count);
    }

    public override void WriteCData(string text)
    {
        this.writer.WriteCData(text);
    }

    public override void WriteCharEntity(char ch)
    {
        this.writer.WriteCharEntity(ch);
    }

    public override void WriteChars(char[] buffer, int index, int count)
    {
        this.writer.WriteChars(buffer, index, count);
    }

    public override void WriteComment(string text)
    {
        this.writer.WriteComment(text);
    }

    public override void WriteDocType(string name, string pubid, string sysid, string subset)
    {
        this.writer.WriteDocType(name, pubid, sysid, subset);
    }

    public override void WriteEndAttribute()
    {
        this.writer.WriteEndAttribute();
    }

    public override void WriteEndDocument()
    {
        this.writer.WriteEndDocument();
    }

    public override void WriteEndElement()
    {
        this.writer.WriteEndElement();
    }

    public override void WriteEntityRef(string name)
    {
        this.writer.WriteEntityRef(name);
    }

    public override void WriteFullEndElement()
    {
        this.writer.WriteFullEndElement();
    }

    public override void WriteProcessingInstruction(string name, string text)
    {
        this.writer.WriteProcessingInstruction(name, text);
    }

    public override void WriteRaw(string data)
    {
        this.writer.WriteRaw(data);
    }

    public override void WriteRaw(char[] buffer, int index, int count)
    {
        this.writer.WriteRaw(buffer, index, count);
    }

    public override void WriteStartAttribute(string prefix, string localName, string ns)
    {
        this.writer.WriteStartAttribute(prefix, localName, ns);
    }

    public override void WriteStartDocument()
    {
        this.writer.WriteStartDocument();
    }

    public override void WriteStartDocument(bool standalone)
    {
        this.writer.WriteStartDocument(standalone);
    }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        this.writer.WriteStartElement(prefix, localName, ns);
    }

    public override void WriteString(string text)
    {
        this.writer.WriteString(text);
    }

    public override void WriteSurrogateCharEntity(char lowChar, char highChar)
    {
        this.writer.WriteSurrogateCharEntity(lowChar, highChar);
    }

    public override void WriteValue(bool value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(DateTime value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(decimal value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(double value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(int value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(long value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(object value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(float value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(string value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteWhitespace(string ws)
    {
        this.writer.WriteWhitespace(ws);
    }


    public override XmlWriterSettings Settings
    {
        get
        {
            return this.writer.Settings;
        }
    }

    protected XmlWriter Writer
    {
        get
        {
            return this.writer;
        }
        set
        {
            this.writer = value;
        }
    }

    public override System.Xml.WriteState WriteState
    {
        get
        {
            return this.writer.WriteState;
        }
    }

    public override string XmlLang
    {
        get
        {
            return this.writer.XmlLang;
        }
    }

    public override System.Xml.XmlSpace XmlSpace
    {
        get
        {
            return this.writer.XmlSpace;
        }
    }
}

그런 다음 이전과 같이 StartElement 메소드를 대체하는 파생 클래스를 제공하십시오.

public class NamespaceSupressingXmlWriter : XmlWriterWrapper
{
    //Provide as many contructors as you need
    public NamespaceSupressingXmlWriter(System.IO.TextWriter output)
        : base(XmlWriter.Create(output)) { }

    public NamespaceSupressingXmlWriter(XmlWriter output)
        : base(XmlWriter.Create(output)) { }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        base.WriteStartElement("", localName, "");
    }
}

그런 다음이 작가를 다음과 같이 사용하십시오.

        var o2= new MyTypeWithNamespaces { ..intializers.. };
        var builder = new System.Text.StringBuilder();
        var settings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
        using ( XmlWriter innerWriter = XmlWriter.Create(builder, settings))
            using ( XmlWriter writer = new NamespaceSupressingXmlWriter(innerWriter))
            {
                s2.Serialize(writer, o2, ns2);
            }
        Console.WriteLine("{0}",builder.ToString());

Oleg Tkachenko 에게 이것에 대한 신용 .


답변

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네임 스페이스.


답변

이것은 질문에 대한 두 가지 대답 중 첫 번째입니다.

네임 스페이스를 세밀하게 제어하려는 경우 (예 : 네임 스페이스 중 일부는 생략하고 다른 네임 스페이스는 생략하려는 경우) 또는 한 네임 스페이스를 다른 네임 스페이스로 바꾸려는 경우 XmlAttributeOverrides를 사용하여이 작업을 수행 할 수 있습니다. .

이 유형 정의가 있다고 가정하십시오.

// explicitly specify a namespace for this type,
// to be used during XML serialization.
[XmlRoot(Namespace="urn:Abracadabra")]
public class MyTypeWithNamespaces
{
    // private fields backing the properties
    private int _Epoch;
    private string _Label;

    // explicitly define a distinct namespace for this element
    [XmlElement(Namespace="urn:Whoohoo")]
    public string Label
    {
        set {  _Label= value; }
        get { return _Label; }
    }

    // this property will be implicitly serialized to XML using the
    // member name for the element name, and inheriting the namespace from
    // the type.
    public int Epoch
    {
        set {  _Epoch= value; }
        get { return _Epoch; }
    }
}

그리고이 직렬화 의사 코드 :

        var o2= new MyTypeWithNamespaces() { ..initializers...};
        ns.Add( "", "urn:Abracadabra" );
        XmlSerializer s2 = new XmlSerializer(typeof(MyTypeWithNamespaces));
        s2.Serialize(System.Console.Out, o2, ns);

이 XML과 같은 것을 얻을 수 있습니다 :

<MyTypeWithNamespaces xmlns="urn:Abracadabra">
  <Label xmlns="urn:Whoohoo">Cimsswybclaeqjh</Label>
  <Epoch>97</Epoch>
</MyTypeWithNamespaces>

루트 요소에는 기본 네임 스페이스가 있고 “Label”요소에는 고유 한 네임 스페이스가 있습니다. 이러한 네임 스페이스는 위의 코드에서 형식을 장식하는 특성에 의해 결정되었습니다.

.NET의 Xml 직렬화 프레임 워크에는 실제 코드를 장식하는 속성 을 명시 적으로 재정 의 할 수있는 기능이 포함되어 있습니다. XmlAttributesOverrides 클래스 및 친구들과 함께이 작업을 수행합니다. 동일한 유형을 가지고 다음과 같이 직렬화한다고 가정하십시오.

        // instantiate the container for all attribute overrides
        XmlAttributeOverrides xOver = new XmlAttributeOverrides();

        // define a set of XML attributes to apply to the root element
        XmlAttributes xAttrs1 = new XmlAttributes();

        // define an XmlRoot element (as if [XmlRoot] had decorated the type)
        // The namespace in the attribute override is the empty string. 
        XmlRootAttribute xRoot = new XmlRootAttribute() { Namespace = ""};

        // add that XmlRoot element to the container of attributes
        xAttrs1.XmlRoot= xRoot;

        // add that bunch of attributes to the container holding all overrides
        xOver.Add(typeof(MyTypeWithNamespaces), xAttrs1);

        // create another set of XML Attributes
        XmlAttributes xAttrs2 = new XmlAttributes();

        // define an XmlElement attribute, for a type of "String", with no namespace
        var xElt = new XmlElementAttribute(typeof(String)) { Namespace = ""};

        // add that XmlElement attribute to the 2nd bunch of attributes
        xAttrs2.XmlElements.Add(xElt);

        // add that bunch of attributes to the container for the type, and
        // specifically apply that bunch to the "Label" property on the type.
        xOver.Add(typeof(MyTypeWithNamespaces), "Label", xAttrs2);

        // instantiate a serializer with the overrides 
        XmlSerializer s3 = new XmlSerializer(typeof(MyTypeWithNamespaces), xOver);

        // serialize
        s3.Serialize(System.Console.Out, o2, ns2);

결과는 다음과 같습니다.

<MyTypeWithNamespaces>
  <Label>Cimsswybclaeqjh</Label>
  <Epoch>97</Epoch>
</MyTypeWithNamespaces>

네임 스페이스를 제거했습니다.

논리적 질문은 직렬화 중에 명시 적 재정의를 거치지 않고 임의의 유형에서 모든 네임 스페이스를 제거 할 수 있습니까? 대답은 ‘예’이며 어떻게해야합니까?


답변

XmlSerializer sr = new XmlSerializer(objectToSerialize.GetType());
TextWriter xmlWriter = new StreamWriter(filename);
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
sr.Serialize(xmlWriter, objectToSerialize, namespaces);


답변