web-dev-qa-db-ja.com

ASP.NET Web APIからXMLの名前空間を削除する

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>
35
Mike Flynn

オプション1は、XmlSerializerGlobalConfigurationを使用するように切り替えることです。

config.Formatters.XmlFormatter.UseXmlSerializer = true;

オプション2は、モデルを装飾することです

[DataContract(Namespace="")]

(そうする場合は、[DataMember]属性でメンバーを修飾する必要があります)。

39
Filip W

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を作成する必要があります。何らかの理由で、デフォルトのフォーマッタは常に2つのデフォルトのネームスペースを追加します。

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

私はpobed2の答えが好きです。しかし、CustomNamespaceXmlFormatter属性が欠落している場合に使用されるデフォルトのルート名前空間を指定できるようにするために、XmlRootが必要でしたand存在し、値がない場合も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);
        });
    }
}
6
Konamiman

応答モデルを保持するプロジェクトでは、Properties/AssemblyInfo.cs

追加

using System.Runtime.Serialization;

そして、下部に追加します

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

置換Project.YourResponseModels応答モデルが配置されている実際のネームスペース。名前空間ごとに1つ追加する必要があります

3
Pawel Cioch

次のアルゴリズムを使用できます

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