37

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

7 回答 7

43

选项 1 是切换到使用XmlSerializerin GlobalConfiguration

config.Formatters.XmlFormatter.UseXmlSerializer = true;

选项 2 是用

[DataContract(Namespace="")]

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

于 2012-09-25T22:05:29.373 回答
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; }
}

您需要做的是创建一个自定义 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});
        ...
    }
}   
于 2013-05-29T15:48:05.860 回答
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);
        });
    }
}
于 2014-11-10T15:05:19.237 回答
3

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

添加

using System.Runtime.Serialization;

并在底部添加

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

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

于 2016-07-21T18:40:04.267 回答
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. 将方法写入控制器或实用程序的类

    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();
    } 
    
于 2019-01-15T16:09:24.850 回答
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;
        }
    }
}
于 2019-04-18T19:35:34.887 回答
-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);
}
于 2017-11-29T16:53:02.743 回答