Skip to main content
 首页 » 编程设计

c#之在 c# web api 中序列化 xml 时删除命名空间内部节点

2024年02月05日25lori

我正在使用 webapi MVC 模板使用默认的 XmlSerializer 我得到以下输出

OUTPUT: 
<ClassName xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
<Child1>String</Child> 
<Child2 xsi:nil=true/> 
</ClassName>  
 
DTO class 
[XmlRoot("ClassName")] 
[DataContract] 
public class ClassName 
{ 
        [DataMember] 
        [XmlElement("Child1")] 
        public string Child1{ get; set; } 
 
        [DataMember] 
        [XmlElement("Child2",IsNullable =true)] 
        public string Child2{ get; set; } 
} 

我通过覆盖默认的 xmlformatter 设法删除了根节点命名空间

//Code to remove root node namespace 
  using (XmlWriter xw = XmlWriter.Create(streamWriter, settings)) 
                    { 
                        XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
                        ns.Add("", ""); 
                        serializer.Serialize(xw, value, ns); 
 
                } 

但是现在根节点中的命名空间被移除了,但是子节点仍然有命名空间

Current Output: 
<ClassName> 
    <Child1>String</Child> 
    <Child2 xsi:nil=true p2:nil="true" xmlns:p2="http://www.w3.org/2001/XMLSchema-instance"/> 
</ClassName> 
Expected output: 
    <ClassName> 
        <Child1>String</Child> 
        <Child2 /> 
    </ClassName> 

请您参考如下方法:

命名空间 “http://www.w3.org/2001/XMLSchema-instance” 需要 添加,因为您指定了 XmlElementAttribute.IsNullable = true对于 Child2。这在 documentation 中有解释。 :

The XML schema specification for structures allows an XML document to explicitly signal that an element's content is missing. Such an element contains the attribute xsi:nil set to true. For more information, see the World Wide Web Consortium (www.w3.org) specification, "XML Schema Part 1: Structures".

If the IsNullable property is set to true, the xsi:nil attribute is generated for class members that have been set to null. For example if you set a field named MyStringArray to null, the XmlSerializer generates the following XML code.

<MyStringArray xsi:nil = "true" /> 

If the IsNullable property is false, no XML element is generated for class members that have been set to null.

"xsi:nil" 中的 xsi 是命名空间 http://www.w3.org/2001/XMLSchema-instance 的快捷方式 如 Xsi:nil Attribute Binding Support 中所述.由于 xsi:nil 是一个 w3c 标准属性(定义为 here ),省略 namespace 会创建非标准 XML,接收系统无法正确理解。请注意,XmlSerializer 仅在需要时发出 nil 属性和关联的命名空间。

的 XML 输出
new ClassName() { Child1 = null, Child2 = string.Empty } 

将不包含属性或命名空间。

顺便说一下,在 XML 中消除可能的混淆

<Child2 xsi:nil=true p2:nil="true" xmlns:p2="http://www.w3.org/2001/XMLSchema-instance"/> 

只有 nil 属性在 "http://www.w3.org/2001/XMLSchema-instance" 命名空间,因为只有属性具有 p2: 前缀。 Child2 元素本身位于默认 namespace 中,因此 namespace 的存在不会改变 XML 的整体含义。事实上,没有理由不在根节点上保留 xsi 和 xsd 命名空间。因为它们是命名命名空间而不是默认命名空间,元素和属性只有在它们具有适当的前缀时才会属于它们。

也就是说,如果出于某种原因您必须使用自定义 nil 属性来表示空文本值,您将需要实现自定义属性来序列化和反序列化它,例如:

    [XmlRoot("ClassName")] 
    [DataContract] 
    public class ClassName 
    { 
        [DataMember] 
        [XmlElement("Child1")] 
        public string Child1 { get; set; } 
 
        [DataMember] 
        [XmlIgnore] 
        public string Child2 { get; set; } 
 
        [XmlElement("Child2")] 
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)] 
        [IgnoreDataMember] 
        public CustomXmlNilTextWrapper Child2Xml { get { return Child2; } set { Child2 = value; } } 
    } 
 
    public struct CustomXmlNilTextWrapper 
    { 
        bool forceNull; 
        string value; 
 
        public static implicit operator CustomXmlNilTextWrapper(string value) 
        { 
            return new CustomXmlNilTextWrapper(value); 
        } 
 
        public static implicit operator string(CustomXmlNilTextWrapper wrapper) 
        { 
            return wrapper.Value; 
        } 
 
        public CustomXmlNilTextWrapper(string value) 
        { 
            this.value = value; 
            this.forceNull = value == null; 
        } 
 
        [XmlAttribute("nil")] 
        public bool ForceNull { get { return forceNull; } set { forceNull = value; } } 
 
        public bool ShouldSerializeForceNull() { return ForceNull == true; } 
 
        [XmlText] 
        public string Value { get { return ForceNull ? null : value ?? string.Empty; } set { this.value = value; } } 
    } 

序列化此类的默认实例将产生如下输出:

<ClassName> 
    <Child2 nil="true" /> 
</ClassName> 

这满足了您没有命名空间的要求 - 但在我看来不值得这么麻烦。

原型(prototype) fiddle展示一切是如何运作的。