【问题标题】:Remove namespace in XML from ASP.NET Web API从 ASP.NET Web API 中删除 XML 中的命名空间
【发布时间】:2012-09-25 20:47:55
【问题描述】:

如何使用 Web API 从下面的 xml 响应中删除命名空间?

<ApiDivisionsResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/GrassrootsHoops.Models.Api.Response">
<Divisions xmlns:d2p1="http://schemas.datacontract.org/2004/07/GrassrootsHoops.Data.Entities">
<d2p1:Page>1</d2p1:Page>
<d2p1:PageSize>10</d2p1:PageSize>
<d2p1:Results xmlns:d3p1="http://schemas.datacontract.org/2004/07/GrassrootsHoops.Models.Api.Response.Divisions"/>
<d2p1:Total>0</d2p1:Total>
</Divisions>
</ApiDivisionsResponse>

【问题讨论】:

标签: xml asp.net-web-api


【解决方案1】:

选项1是在GlobalConfiguration中切换到使用XmlSerializer

config.Formatters.XmlFormatter.UseXmlSerializer = true;

选项 2 是用

装饰你的模型
[DataContract(Namespace="")]

(如果这样做,您需要使用[DataMember] 属性来装饰成员)。

【讨论】:

  • 我做了 UseXmlSerializer,现在它只使用 JSON。
  • [DataContract()] 属性需要引用 System.Runtime.Serialization
  • @MikeFlynn 我遇到了同样的问题。如果 XmlSerializer 无法序列化对象,它将尝试 Json。 IMO 并不是真正的预期默认行为。尤其是当 NetDataContractSerializer 抛出错误时。
  • 我设置的是UseXmlSerializer = true;,但格式和OP写的一样
  • 不知道为什么,但我尝试了这两种方法,但没有一个对我有用。 Konamiman 的回答完成了这项工作。
【解决方案2】:

如果您愿意使用 XmlRoot 来装饰您的模型,这是一个不错的方法。假设你有一辆带门的汽车。默认的 WebApi 配置将返回如下内容:

<car 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <doors>
        <door>
            <color>black</color>
        </door>
    </doors>
</car>

这就是你想要的:

<car>
    <doors>
        <door>
            <color>black</color>
        </door>
    </doors>
</car>

这是模型:

[XmlRoot("car")]
public class Car
{
    [XmlArray("doors"), XmlArrayItem("door")]
    public Door[] Doors { get; set; }
}

您需要做的是创建一个自定义的 XmlFormatter,如果 XmlRoot 属性中没有定义命名空间,它将有一个空的命名空间。出于某种原因,默认格式化程序总是添加两个默认命名空间。

public class CustomNamespaceXmlFormatter : XmlMediaTypeFormatter
{
    public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content,
                                            TransportContext transportContext)
    {
        try
        {
            var xns = new XmlSerializerNamespaces();
            foreach (var attribute in type.GetCustomAttributes(true))
            {
                var xmlRootAttribute = attribute as XmlRootAttribute;
                if (xmlRootAttribute != null)
                {
                    xns.Add(string.Empty, xmlRootAttribute.Namespace);
                }
            }

            if (xns.Count == 0)
            {
                xns.Add(string.Empty, string.Empty);
            }

            var task = Task.Factory.StartNew(() =>
                {
                    var serializer = new XmlSerializer(type);
                    serializer.Serialize(writeStream, value, xns);
                });

            return task;
        }
        catch (Exception)
        {
            return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
        }
    }
}

最后要做的是在 WebApiContext 中添加新的格式化程序。请务必删除(或清除)旧的 XmlMediaTypeFormatter

public static class WebApiContext
{
    public static void Register(HttpConfiguration config)
    {
        ...
        config.Formatters.Clear();
        config.Formatters.Add(new CustomNamespaceXmlFormatter{UseXmlSerializer=true});
        ...
    }
}   

【讨论】:

  • 嗨,我喜欢你的解决方案,但是如果发生异常,我不明白调用基本实现的意义。你能解释一下你为什么这样做吗?谢谢。
  • 不幸的是,我已经有一段时间没有破解这个了。根据我的记忆,我认为它旨在作为一种简单地跳过 XML 命名空间的删除并简单地调用“正常”格式化程序的方法。
  • 伟大的工作。但是,请注意:config.Formatters.Add(new IgnoreNamespacesXmlMediaTypeFormatter { UseXmlSerializer = true }); 应该设置,否则发送POST 数据时,FromBody 将无法发送serialize
  • 这是唯一对我有用的解决方案,经过 5 个小时的搜索。我最终使用了 Konamiman 的版本。
  • 注意:config.Formatters.Clear();消除所有其他格式化程序,包括 json。
【解决方案3】:

我喜欢 pobed2 的回答。但是我需要 CustomNamespaceXmlFormatter 来允许我指定一个默认的根命名空间,以便在 XmlRoot 属性丢失时使用(即该属性仅用于设置根元素名称)。所以我创建了一个改进版本,以防它对某人有用:

public class CustomNamespaceXmlFormatter : XmlMediaTypeFormatter
{
    private readonly string defaultRootNamespace;

    public CustomNamespaceXmlFormatter() : this(string.Empty)
    {
    }

    public CustomNamespaceXmlFormatter(string defaultRootNamespace)
    {
        this.defaultRootNamespace = defaultRootNamespace;
    }

    public override Task WriteToStreamAsync(
        Type type, 
        object value, 
        Stream writeStream,
        HttpContent content,
        TransportContext transportContext)
    {
        var xmlRootAttribute = type.GetCustomAttribute<XmlRootAttribute>(true);
        if(xmlRootAttribute == null)
            xmlRootAttribute = new XmlRootAttribute(type.Name)
            {
                Namespace = defaultRootNamespace
            };
        else if(xmlRootAttribute.Namespace == null)
            xmlRootAttribute = new XmlRootAttribute(xmlRootAttribute.ElementName)
            {
                Namespace = defaultRootNamespace
            };

        var xns = new XmlSerializerNamespaces();
        xns.Add(string.Empty, xmlRootAttribute.Namespace);

        return Task.Factory.StartNew(() =>
        {
            var serializer = new XmlSerializer(type, xmlRootAttribute);
            serializer.Serialize(writeStream, value, xns);
        });
    }
}

【讨论】:

  • this answer 中所述,为避免严重的内存泄漏,使用非默认构造函数构造的XmlSerializer 必须静态缓存和重用。另请参阅the docs 哪个状态 如果您使用任何其他构造函数,则会生成同一程序集的多个版本并且永远不会卸载,这会导致内存泄漏和性能下降。 ...否则,您必须将程序集缓存在哈希表中,
  • 这个人就遇到了这个问题,并在他的服务器中吃了 32Gb 的 RAM。 The Evil XMLSerializer
【解决方案4】:

在保留响应模型的项目中转到Properties/AssemblyInfo.cs

添加

using System.Runtime.Serialization;

并在底部添加

[assembly: ContractNamespace("", ClrNamespace = "Project.YourResponseModels")]

Project.YourResponseModels 替换为响应模型所在的实际命名空间。 您需要为每个命名空间添加一个

【讨论】:

  • 您好,看起来像是解决方法,应该被视为一种不好的做法。例如,在项目结构重构的情况下,我的YourResponseModels 可以从Project.YourResponseModels 命名空间移动到Project.AnotherPlace.YourResponseModels。在任何重构的情况下,每个人都应该牢记这一点。
  • 这是为了 Web API 目的而使 XML 主体干净的旧方法,所以当你重构时并不重要,代码和实体的结构是你希望的,只是序列化器可以处理任何事情。至于重组,我认为 AssemblyInfo.cs 不会包含超过 10-20 行,仍然易于维护。现在谁会使用 XML? WebAPI 2.0+ 解决了所有这些问题,JSON 应该是现代应用程序的唯一格式。如果您与旧系统交互,则无论如何都可以保留 XML 命名空间。
【解决方案5】:

你可以使用下一个算法

  1. 为你的班级添加属性

    [XmlRoot("xml", Namespace = "")]
    public class MyClass
    {
       [XmlElement(ElementName = "first_node", Namespace = "")]
       public string FirstProperty { get; set; }
    
       [XmlElement(ElementName = "second_node", Namespace = "")]
       public string SecondProperty { get; set; }
    }
    
  2. 将方法写入控制器或实用程序的类中

    private ContentResult SerializeWithoutNamespaces(MyClass instanseMyClass)
    {
        var sw = new StringWriter();
        var xmlWriter = XmlWriter.Create(sw, new XmlWriterSettings() {OmitXmlDeclaration = true});
    
        var ns = new XmlSerializerNamespaces();
        ns.Add("", "");
        var serializer = new XmlSerializer(instanseMyClass.GetType());
        serializer.Serialize(xmlWriter, instanseMyClass, ns);
    
        return Content(sw.ToString());
    }
    
  3. 使用方法SerializeWithoutNamespaces进入Action

    [Produces("application/xml")]
    [Route("api/My")]
    public class MyController : Controller
    {
      [HttpPost]
      public ContentResult MyAction(string phrase)
      {                           
        var instanseMyClass = new MyClass{FirstProperty ="123", SecondProperty ="789"};
        return SerializeWithoutNamespaces(instanseMyClass); 
      }
    }
    
  4. 别忘了在 StartUp 类中添加一些依赖项

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc()
            .AddXmlSerializerFormatters()
            .AddXmlDataContractSerializerFormatters();
    } 
    

【讨论】:

  • 显示输出?它是否包含默认的 xsi 命名空间?
  • @LeszekRepie 输出将是&lt;xml&gt;&lt;first_node&gt;123&lt;/first_node&gt;&lt;second_node&gt;789&lt;/second_node&gt;&lt;/xml&gt;
【解决方案6】:

CustomNamespaceXmlFormatter 类对我有用,只是它导致了内存泄漏(当我的 Web 服务受到重创时,内存不断增加),所以我修改了 XmlSerializer 实例的创建方式:

public class CustomNamespaceXmlFormatter : XmlMediaTypeFormatter
{
    private readonly string defaultRootNamespace;

    public CustomNamespaceXmlFormatter() : this(string.Empty)
    {
    }

    public CustomNamespaceXmlFormatter(string defaultRootNamespace)
    {
        this.defaultRootNamespace = defaultRootNamespace;
    }

    public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
    {
        if (type == typeof(String))
        {
            //If all we want to do is return a string, just send to output as <string>value</string>
            return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
        }
        else
        {
            XmlRootAttribute xmlRootAttribute = (XmlRootAttribute)type.GetCustomAttributes(typeof(XmlRootAttribute), true)[0];
            if (xmlRootAttribute == null)
                xmlRootAttribute = new XmlRootAttribute(type.Name)
                {
                    Namespace = defaultRootNamespace
                };
            else if (xmlRootAttribute.Namespace == null)
                xmlRootAttribute = new XmlRootAttribute(xmlRootAttribute.ElementName)
                {
                    Namespace = defaultRootNamespace
                };

            var xns = new XmlSerializerNamespaces();
            xns.Add(string.Empty, xmlRootAttribute.Namespace);

            return Task.Factory.StartNew(() =>
            {
                //var serializer = new XmlSerializer(type, xmlRootAttribute); **OLD CODE**
                var serializer = XmlSerializerInstance.GetSerializer(type, xmlRootAttribute);
                serializer.Serialize(writeStream, value, xns);                    
            });
        }
    }
}

public static class XmlSerializerInstance
{
    public static object _lock = new object();
    public static Dictionary<string, XmlSerializer> _serializers = new Dictionary<string, XmlSerializer>();
    public static XmlSerializer GetSerializer(Type type, XmlRootAttribute xra)
    {
        lock (_lock)
        {
            var key = $"{type}|{xra}";
            if (!_serializers.TryGetValue(key, out XmlSerializer serializer))
            {
                if (type != null && xra != null)
                {
                    serializer = new XmlSerializer(type, xra);
                }

                _serializers.Add(key, serializer);
            }

            return serializer;
        }
    }
}

【讨论】:

    【解决方案7】:

    这很好用

    public ActionResult JsonAction(string xxx)
    { 
        XmlDocument xmlDoc2 = new XmlDocument();
        xmlDoc2.Load(xmlStreamReader);
    
        XDocument d = XDocument.Parse(optdoc2.InnerXml);
        d.Root.Attributes().Where(x => x.IsNamespaceDeclaration).Remove();
    
        foreach (var elem in d.Descendants())
        elem.Name = elem.Name.LocalName;
    
        var xmlDocument = new XmlDocument();
        xmlDocument.Load(d.CreateReader());
    
        var jsonText = JsonConvert.SerializeXmlNode(xmlDocument);
        return Content(jsonText);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-03
      • 2020-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多