Подтвердить что ты не робот

Удалить пространство имен в 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>
4b9b3361

Ответ 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

Вы могли бы использовать следующий алгоритм

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

Ответ 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);
}