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>
オプション1は、XmlSerializer
でGlobalConfiguration
を使用するように切り替えることです。
config.Formatters.XmlFormatter.UseXmlSerializer = true;
オプション2は、モデルを装飾することです
[DataContract(Namespace="")]
(そうする場合は、[DataMember]
属性でメンバーを修飾する必要があります)。
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});
...
}
}
私は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);
});
}
}
応答モデルを保持するプロジェクトでは、Properties/AssemblyInfo.cs
追加
using System.Runtime.Serialization;
そして、下部に追加します
[Assembly: ContractNamespace("", ClrNamespace = "Project.YourResponseModels")]
置換Project.YourResponseModels
応答モデルが配置されている実際のネームスペース。名前空間ごとに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; }
}
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());
}
メソッド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);
}
}
StartUpクラスにいくつかの依存関係を置くことを忘れないでください
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddXmlSerializerFormatters()
.AddXmlDataContractSerializerFormatters();
}
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;
}
}
}