XmlSerializer: xóa các không gian tên xsi và xsd không cần thiết


132

Có cách nào để định cấu hình XmlSerializer để nó không ghi các không gian tên mặc định trong phần tử gốc không?

Những gì tôi nhận được là đây:

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

và tôi muốn xóa cả khai báo xmlns.

Bản sao của : Làm cách nào để tuần tự hóa một đối tượng thành XML mà không nhận được xmlns = phạm lỗi?

Câu trả lời:


63

Vì Dave yêu cầu tôi lặp lại câu trả lời của mình để Bỏ qua tất cả các không gian tên xsi và xsd khi tuần tự hóa một đối tượng trong .NET , tôi đã cập nhật bài đăng này và lặp lại câu trả lời của tôi ở đây từ liên kết được đề cập ở trên. Ví dụ được sử dụng trong câu trả lời này là ví dụ tương tự được sử dụng cho câu hỏi khác. Những gì tiếp theo được sao chép, nguyên văn.


Sau khi đọc tài liệu của Microsoft và một số giải pháp trực tuyến, tôi đã phát hiện ra giải pháp cho vấn đề này. Nó hoạt động với cả XmlSerializertuần tự hóa XML tích hợp và tùy chỉnh thông qua IXmlSerialiazble.

Để làm trắng, tôi sẽ sử dụng cùng một MyTypeWithNamespacesmẫu XML đã được sử dụng trong các câu trả lời cho câu hỏi này cho đến nay.

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

Đó là tất cả cho lớp học này. Bây giờ, một số người phản đối việc có một XmlSerializerNamespacesđối tượng ở đâu đó trong các lớp của họ; nhưng như bạn có thể thấy, tôi đã giấu nó gọn gàng trong hàm tạo mặc định và hiển thị một thuộc tính công cộng để trả về các không gian tên.

Bây giờ, khi đến lúc nối tiếp lớp, bạn sẽ sử dụng đoạn mã sau:

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);

Một khi bạn đã làm điều này, bạn sẽ nhận được đầu ra sau đây:

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

Tôi đã sử dụng thành công phương thức này trong một dự án gần đây với một loạt các lớp được tuần tự hóa thành XML cho các cuộc gọi dịch vụ web. Tài liệu của Microsoft không rõ ràng về việc phải làm gì với XmlSerializerNamespacesthành viên bị cáo buộc công khai một khi bạn đã tạo ra nó và rất nhiều người nghĩ rằng nó vô dụng. Nhưng bằng cách làm theo tài liệu của họ và sử dụng nó theo cách hiển thị ở trên, bạn có thể tùy chỉnh cách XmlSerializer tạo XML cho các lớp của bạn mà không cần dùng đến hành vi không được hỗ trợ hoặc "tuần tự hóa" của riêng bạn bằng cách triển khai IXmlSerializable.

Tôi hy vọng rằng câu trả lời này sẽ được đặt vào phần còn lại, một lần và mãi mãi, làm thế nào để thoát khỏi tiêu chuẩn xsixsdkhông gian tên được tạo bởi XmlSerializer.

CẬP NHẬT: Tôi chỉ muốn đảm bảo rằng tôi đã trả lời câu hỏi của OP về việc xóa tất cả các không gian tên. Mã của tôi ở trên sẽ làm việc cho điều này; Hãy để tôi chỉ cho bạn cách làm. Bây giờ, trong ví dụ trên, bạn thực sự không thể thoát khỏi tất cả các không gian tên (vì có hai không gian tên được sử dụng). Ở đâu đó trong tài liệu XML của bạn, bạn sẽ cần phải có một cái gì đó như thế xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo. Nếu lớp trong ví dụ là một phần của tài liệu lớn hơn, thì ở đâu đó phía trên một không gian tên phải được khai báo cho một trong hai (hoặc cả hai) AbracadbraWhoohoo. Nếu không, thì phần tử trong một hoặc cả hai không gian tên phải được trang trí bằng một tiền tố của một loại nào đó (bạn không thể có hai không gian tên mặc định, phải không?). Vì vậy, trong ví dụ này, Abracadabralà không gian tên mặc định. Tôi có thể bên trong MyTypeWithNamespaceslớp mình thêm tiền tố không gian tên cho Whoohookhông gian tên như vậy:

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

Bây giờ, trong định nghĩa lớp của tôi, tôi đã chỉ ra rằng <Label/>phần tử nằm trong không gian tên "urn:Whoohoo", vì vậy tôi không cần phải làm gì thêm. Khi tôi bây giờ tuần tự hóa lớp sử dụng mã tuần tự hóa ở trên của tôi không thay đổi, đây là đầu ra:

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

Bởi vì <Label>trong một không gian tên khác với phần còn lại của tài liệu, nên trong một ngày nào đó, nó phải được "trang trí" bằng một không gian tên. Lưu ý rằng vẫn không có xsixsdkhông gian tên.


Điều này kết thúc câu trả lời của tôi cho câu hỏi khác. Nhưng tôi muốn chắc chắn rằng tôi đã trả lời câu hỏi của OP về việc không sử dụng không gian tên, vì tôi cảm thấy mình chưa thực sự giải quyết nó. Giả sử rằng đó <Label>là một phần của không gian tên giống như phần còn lại của tài liệu, trong trường hợp này urn:Abracadabra:

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

Hàm tạo của bạn sẽ trông giống như trong ví dụ mã đầu tiên của tôi, cùng với thuộc tính công khai để lấy không gian tên mặc định:

// 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;

Sau đó, sau đó, trong mã của bạn sử dụng MyTypeWithNamespacesđối tượng để tuần tự hóa nó, bạn sẽ gọi nó như tôi đã làm ở trên:

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);

XmlSerializersẽ nhổ trở lại cùng một XML như được hiển thị ngay trên đây mà không có không gian tên bổ sung trong đầu ra:

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

Để đầy đủ, có lẽ bạn nên bao gồm câu trả lời đúng ở đây thay vì chỉ đề cập đến nó, và tôi cũng muốn biết làm thế nào bạn kết luận rằng đó là 'hành vi không được hỗ trợ'.
Dave Van den Eynde

1
Đến đây một lần nữa để kiểm tra điều này, vì đó là lời giải thích đơn giản nhất mà tôi đã tìm thấy. Cảm ơn @fourpastmidnight
Andre Albuquerque

2
Tôi không hiểu, vì câu trả lời cuối cùng của OP, bạn vẫn đang sử dụng một không gian tên trong quá trình tuần tự hóa "urn: Abracadabra" (hàm tạo), tại sao điều đó không được bao gồm trong đầu ra cuối cùng. Không nên sử dụng OP: XmlSerializerNamespaces EmptyXmlSerializerNamespaces = new XmlSerializerNamespaces (new [] {XmlQualifiedName.Empty});
dparkar

2
Đây là câu trả lời đúng, mặc dù không được bình chọn nhiều nhất. Điều khó khăn mà tôi không làm được là XmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws);tôi phải thay thế var xtw = XmlTextWriter.Create(memStm, xws);.
Leonel Sanches da Silva

1
Đã được một thời gian kể từ khi tôi viết câu trả lời này. XmlTextWriter.Createtrả về một thể hiện (trừu tượng?) XmlWriter. Vì vậy, @ Preza8 là chính xác, bạn sẽ mất khả năng thiết lập các XmlTextWriterthuộc tính cụ thể khác (ít nhất, không phải là không sử dụng nó), do đó, việc phân vai cụ thể thành XmlTextWriter.
bốnpastmidnight

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
Hmmm ... các bạn là những kẻ nổi loạn. Nó nói rõ ràng tại msdn.microsoft.com/en-us/l Library / từ mà bạn không thể làm điều đó.
Ralph Lavelle

Bool Yah! (Để khắc phục những gì ms nói là bạn không thể làm)
granadaCoder

3
Tôi không chắc tại sao nó "không được hỗ trợ", nhưng điều này thực hiện chính xác những gì tôi muốn.
Dan Bechard

8
Câu trả lời này tạo ra các không gian tên "xmlns: d1p1" và "xmlns: q1". Cái này là cái gì?
Leonel Sanches da Silva

2
Chà, mã này hoạt động cho các tuần tự thực sự thực sự đơn giản, không có các định nghĩa không gian tên khác. Đối với nhiều định nghĩa không gian tên, câu trả lời làm việc là câu trả lời được chấp nhận.
Leonel Sanches da Silva

6

Có một cách khác - bạn có thể cung cấp một thành viên của loại XmlSerializerNamespaces trong loại sẽ được tuần tự hóa. Trang trí nó với thuộc tính XmlNamespaceDeclarations . Thêm tiền tố không gian tên và URI cho thành viên đó. Sau đó, bất kỳ tuần tự hóa nào không cung cấp rõ ràng XmlSerializerNamespaces sẽ sử dụng tiền tố không gian tên + các cặp URI mà bạn đã đặt vào loại của mình.

Mã ví dụ, giả sử đây là loại của bạn:

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

Bạn có thể làm được việc này:

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");

Và điều đó có nghĩa là bất kỳ tuần tự hóa nào của trường hợp đó không chỉ định bộ tiền tố + cặp URI của riêng nó sẽ sử dụng tiền tố "p" cho không gian tên "urn: mycompany.2009". Nó cũng sẽ bỏ qua các không gian tên xsi và xsd.

Sự khác biệt ở đây là bạn đang thêm XmlSerializerNamespaces vào chính loại đó, thay vì sử dụng nó một cách rõ ràng trong một cuộc gọi đến XmlSerializer.Serialize (). Điều này có nghĩa là nếu một thể hiện của loại của bạn được tuần tự hóa bởi mã mà bạn không sở hữu (ví dụ: trong ngăn xếp dịch vụ web) và mã đó không cung cấp rõ ràng XmlSerializerNamespaces, trình tuần tự hóa đó sẽ sử dụng các không gian tên được cung cấp trong thể hiện.


1. Tôi không thấy sự khác biệt. Bạn vẫn đang thêm không gian tên mặc định vào một phiên bản của XmlSerializerNamespaces.
Dave Van den Eynde

3
2. Điều này gây ô nhiễm các lớp nhiều hơn. Mục tiêu của tôi là không sử dụng không gian tên nhất định, mục tiêu của tôi là không sử dụng không gian tên nào cả.
Dave Van den Eynde

Tôi đã thêm một lưu ý về sự khác biệt giữa phương pháp này và chỉ định XmlSerializerNamespaces trong quá trình tuần tự hóa.
Cheeso

0

Tôi đang sử dụng:

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());
            }
        }
    }
}

Để có được XML sau:

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

Nếu bạn không muốn không gian tên, chỉ cần đặt DEFAULT_NAMESPACE thành "".


Mặc dù câu hỏi này đã hơn 10 năm tuổi, nhưng điểm của câu hỏi trước đó là có một phần thân XML hoàn toàn không chứa bất kỳ khai báo không gian tên nào.
Dave Van den Eynde

1
Nếu tôi thêm câu trả lời của riêng mình cho một câu hỏi đã được 10 năm thì đó là vì câu trả lời được chấp nhận dài hơn để đọc hơn Kinh thánh trong phiên bản hoàn chỉnh của nó.
Tối đa

Và câu trả lời được bình chọn nhiều nhất thúc đẩy một cách tiếp cận (không gian tên trống) không được khuyến khích.
Tối đa

Tôi không thể giúp điều đó. Tôi chỉ có thể đưa ra câu trả lời được chấp nhận theo câu tôi tin là đúng nhất.
Dave Van den Eynde
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.