Удалить пространство имен в XML из веб-интерфейса ASP.NET
Как удалить пространство имен из XML-ответа ниже с помощью веб-API?
<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
Вариант 1 заключается в том, чтобы переключиться на использование XmlSerializer
в GlobalConfiguration
:
config.Formatters.XmlFormatter.UseXmlSerializer = true;
Вариант 2 - это украсить ваши модели с помощью
[DataContract(Namespace="")]
(и если вы это сделаете, вам нужно будет украсить элементы атрибутами [DataMember]
).
Ответ 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});
...
}
}
Ответ 3
Мне нравится 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);
});
}
}
Ответ 4
В проекте, который сохраняет модели ответов, перейдите к Properties/AssemblyInfo.cs
Добавить
using System.Runtime.Serialization;
и внизу добавить
[assembly: ContractNamespace("", ClrNamespace = "Project.YourResponseModels")]
Замените Project.YourResponseModels
фактическим пространством имен, в котором находятся модели ответов.
Вам нужно добавить один за пространство имен
Ответ 5
Вы могли бы использовать следующий алгоритм
-
Поместите атрибут для вашего класса
[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; }
}
-
Напишите метод в свой контроллер или класс утилит
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();
}
Ответ 6
Класс CustomNamespaceXmlFormatter помог мне, за исключением того, что он вызвал утечку памяти (когда мой веб-сервис сильно пострадал, память продолжала расти все выше и выше), поэтому я изменил способ создания экземпляров 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);
}