从ASP.NET Web API中删除XML中的命名空间

时间:2012-09-25 20:47:56

标签: xml asp.net-web-api

如何使用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>

7 个答案:

答案 0 :(得分:39)

选项1是切换为使用XmlSerializer中的GlobalConfiguration

config.Formatters.XmlFormatter.UseXmlSerializer = true;

选项2是用

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

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

答案 1 :(得分:21)

如果你愿意用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; }
}

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

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

答案 2 :(得分:6)

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

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

答案 3 :(得分:3)

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

添加

using System.Runtime.Serialization;

并在底部添加

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

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

答案 4 :(得分:0)

您可以使用下一个算法

  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. 将方法写入Controller或util的类

    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 进入操作

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

答案 5 :(得分:0)

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

答案 6 :(得分:-3)

这完美无缺

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