НейроАгент

Руководство: десериализация XML с несколькими пространствами имен в C#

Освойте десериализацию XML с несколькими пространствами имен в C#. Узнайте о настройке XmlSerializer, обработке пространств имен и получите полные примеры реализации для сложных XML-структур.

Как правильно десериализовать XML с несколькими пространствами имен в C#

Я пытаюсь десериализовать XML-ответ от API, который содержит несколько пространств имен. Структура XML-данных выглядит следующим образом:

xml
<ns1:alerts xmlns:ns1="http://gov.fema.ipaws.services/feed">
    <alert xmlns="urn:oasis:names:tc:emergency:cap:1.2">
    <identifier>All-Channels-Test-Message-RT_Automation_124_789f95e3-e02f-4e61-8121

Мой текущий код C# для десериализации выглядит следующим образом:

csharp
var serializer = new XmlSerializer(
    typeof(List<Alert>),
    new XmlRootAttribute("alerts") { Namespace = "http://gov.fema.ipaws.services/feed" });

var alerts = serializer.Deserialize(stream) as Alert[];

В моем классе Alert я установил атрибуты с пространством имен и корневым элементом:

csharp
[XmlRoot("alert", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
public class Alert

Когда я десериализую XML, я получаю 0 экземпляров, хотя в ответе содержится 1-2 предупреждения в зависимости от ленты API. Я проверил, что ответ отформатирован правильно. Моя догадка заключается в том, что я десериализую неправильно из-за нескольких пространств имен. Как правильно десериализовать XML с несколькими пространствами имен в C#?

НейроАгент

Десериализация XML с несколькими пространствами имен в C# требует тщательной настройки XmlSerializer для правильной обработки сложной структуры пространств имен. Ваша текущая проблема, скорее всего, связана с несоответствующими объявлениями пространств имен между корневым и вложенными элементами.

Чтобы правильно десериализовать XML с несколькими пространствами имен, такими как формат CAP (Common Alerting Protocol), с которым вы работаете, вам необходимо настроить атрибуты сериализации так, чтобы они точно соответствовали структуре XML.

Содержание

Понимание структуры пространств имен

Ваш XML содержит два различных пространства имен:

  • http://gov.fema.ipaws.services/feed - для корневого элемента alerts
  • urn:oasis:names:tc:emergency:cap:1.2 - для элементов alert внутри

XmlSerializer должен понимать эту иерархическую структуру пространств имен, чтобы правильно сопоставлять XML-элементы с вашими объектами C#.

Решение 1: Правильная настройка корневого элемента

Основная проблема заключается в том, что вы пытаетесь десериализовать List<Alert> напрямую, когда ваша структура XML имеет корневой элемент, содержащий несколько элементов alert. Вам нужны два класса:

csharp
[XmlRoot("alerts", Namespace = "http://gov.fema.ipaws.services/feed")]
public class AlertsContainer
{
    [XmlElement("alert", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public List<Alert> Alerts { get; set; }
}

[XmlRoot("alert", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
public class Alert
{
    // Ваши свойства alert здесь
    public string identifier { get; set; }
    // Добавьте другие свойства CAP alert при необходимости
}

Затем десериализуйте следующим образом:

csharp
var serializer = new XmlSerializer(typeof(AlertsContainer));
var container = (AlertsContainer)serializer.Deserialize(stream);
var alerts = container.Alerts;

Решение 2: Использование атрибутов XmlElement

Для более сложных XML-структур с несколькими пространствами имен вы можете указывать пространства имен непосредственно в атрибутах XmlElement:

csharp
public class Alert
{
    [XmlElement("identifier", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Identifier { get; set; }
    
    [XmlElement("sender", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Sender { get; set; }
    
    [XmlElement("sent", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public DateTime Sent { get; set; }
    
    // Добавьте другие элементы CAP с их соответствующими пространствами имен
}

Решение 3: Пользовательская обработка пространств имен

Если вам нужно обрабатывать XML, в котором пространства имен могут меняться или быть непредсказуемыми, вы можете создать пользовательский XmlTextReader, который игнорирует пространства имен при десериализации:

csharp
public class IgnoreNamespaceXmlTextReader : XmlTextReader
{
    public IgnoreNamespaceXmlTextReader(TextReader reader) : base(reader) { }
    
    public override string NamespaceURI => string.Empty;
    
    public override string LocalName => 
        base.LocalName.Contains(":") ? 
        base.LocalName.Substring(base.LocalName.IndexOf(":") + 1) : 
        base.LocalName;
}

Затем используйте его следующим образом:

csharp
var serializer = new XmlSerializer(typeof(AlertsContainer));
using (var stringReader = new StreamReader(stream))
using (var xmlReader = new IgnoreNamespaceXmlTextReader(stringReader))
{
    var container = (AlertsContainer)serializer.Deserialize(xmlReader);
}

Решение 4: Расширенная поддержка нескольких пространств имен

Для обработки нескольких версий схем или сложных сценариев с пространствами имен используйте XmlSerializerNamespaces:

csharp
// Для десериализации с несколькими версиями пространств имен
var serializer = new XmlSerializer(typeof(AlertsContainer), 
    new XmlRootAttribute { ElementName = "alerts", Namespace = "http://gov.fema.ipaws.services/feed" });

// Для сериализации с несколькими префиксами пространств имен
var namespaces = new XmlSerializerNamespaces();
namespaces.Add("ns1", "http://gov.fema.ipaws.services/feed");
namespaces.Add("cap", "urn:oasis:names:tc:emergency:cap:1.2");

Полный пример реализации

Вот полный рабочий пример для вашего сценария CAP alert:

csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;

public class CAPAlertDeserializer
{
    public List<Alert> DeserializeAlerts(Stream xmlStream)
    {
        var serializer = new XmlSerializer(typeof(AlertsContainer));
        using (var reader = new StreamReader(xmlStream))
        {
            var container = (AlertsContainer)serializer.Deserialize(reader);
            return container.Alerts;
        }
    }
}

[XmlRoot("alerts", Namespace = "http://gov.fema.ipaws.services/feed")]
public class AlertsContainer
{
    [XmlElement("alert", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public List<Alert> Alerts { get; set; }
}

[XmlRoot("alert", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
public class Alert
{
    [XmlElement("identifier", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Identifier { get; set; }

    [XmlElement("sender", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Sender { get; set; }

    [XmlElement("sent", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public DateTime Sent { get; set; }

    [XmlElement("status", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Status { get; set; }

    [XmlElement("msgType", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string MessageType { get; set; }

    [XmlElement("scope", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Scope { get; set; }

    [XmlElement("info", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public AlertInfo Info { get; set; }
}

[XmlRoot("info", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
public class AlertInfo
{
    [XmlElement("language", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Language { get; set; }

    [XmlElement("category", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Category { get; set; }

    [XmlElement("event", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Event { get; set; }

    [XmlElement("responseType", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string ResponseType { get; set; }

    [XmlElement("urgency", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Urgency { get; set; }

    [XmlElement("severity", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Severity { get; set; }

    [XmlElement("certainty", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Certainty { get; set; }

    [XmlElement("audience", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Audience { get; set; }

    [XmlElement("eventCode", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public List<EventCode> EventCodes { get; set; }

    [XmlElement("effective", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public DateTime Effective { get; set; }

    [XmlElement("expires", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public DateTime? Expires { get; set; }

    [XmlElement("onset", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public DateTime? Onset { get; set; }

    [XmlElement("senderName", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string SenderName { get; set; }

    [XmlElement("headline", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Headline { get; set; }

    [XmlElement("description", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Description { get; set; }

    [XmlElement("instruction", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Instruction { get; set; }

    [XmlElement("web", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Web { get; set; }

    [XmlElement("contact", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Contact { get; set; }

    [XmlElement("parameter", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public List<Parameter> Parameters { get; set; }

    [XmlElement("area", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public List<Area> Areas { get; set; }
}

[XmlRoot("eventCode", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
public class EventCode
{
    [XmlElement("valueName", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string ValueName { get; set; }

    [XmlElement("value", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Value { get; set; }
}

[XmlRoot("parameter", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
public class Parameter
{
    [XmlElement("valueName", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string ValueName { get; set; }

    [XmlElement("value", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Value { get; set; }
}

[XmlRoot("area", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
public class Area
{
    [XmlElement("areaDesc", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string AreaDescription { get; set; }

    [XmlElement("circle", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public Circle Circle { get; set; }

    [XmlElement("polygon", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public string Polygon { get; set; }

    [XmlElement("geocode", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
    public List<Geocode> Geocodes { get; set; }
}

[XmlRoot("circle", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
public class Circle
{
    [XmlAttribute("radius")]
    public string Radius { get; set; }

    [XmlAttribute("latitude")]
    public string Latitude { get; set; }

    [XmlAttribute("longitude")]
    public string Longitude { get; set; }
}

[XmlRoot("geocode", Namespace = "urn:oasis:names:tc:emergency:cap:1.2")]
public class Geocode
{
    [XmlAttribute("valueName")]
    public string ValueName { get; set; }

    [XmlText]
    public string Value { get; set; }
}

Лучшие практики и устранение неполадок

При работе с несколькими пространствами имен в десериализации XML учитывайте следующие лучшие практики:

  1. Всегда точно соответствуйте структуре XML - Убедитесь, что ваши классы C# точно отражают иерархию XML и объявления пространств имен.

  2. Используйте конкретные объявления пространств имен в атрибутах XmlElement и XmlRoot, а не полагайтесь на значения по умолчанию.

  3. Правильно обрабатывайте префиксы пространств имен - XmlSerializer работает с URI пространств имен, а не с префиксами.

  4. Тестируйте с примерами XML - Всегда десериализуйте фактические XML-ответы, чтобы убедиться, что ваша структура классов соответствует.

  5. Добавляйте объявления XML пространств имен в ваши классы с помощью атрибута XmlNamespaceDeclarations, если вам нужно сохранять информацию о пространствах имен во время сериализации.

  6. Используйте XmlSerializerNamespaces, когда вам нужно контролировать префиксы пространств имен в выходном XML.

  7. Рассмотрите возможность использования XML-схем - Инструменты вроде xsd.exe могут генерировать классы C# из XML-схем, обеспечивая правильную обработку пространств имен.

Если у вас продолжают возникать проблемы, попробуйте сначала десериализовать в универсальную XML-структуру, чтобы изучить фактическую структуру XML, а затем сопоставить ее с вашими конкретными классами:

csharp
var doc = new XmlDocument();
doc.Load(stream);
Console.WriteLine(doc.OuterXml); // Изучите фактическую структуру XML

Источники

  1. Десериализация XML с несколькими пространствами имен в C# - Stack Overflow
  2. Десериализация XML с пространством имен в .NET core | GoTask
  3. Игнорирование пространств имен в XML при десериализации в C# | Brian Pedersen’s Sitecore and .NET Blog
  4. Десериализация XML с пространством имен и несколькими вложенными элементами - Stack Overflow
  5. Использование нескольких пространств имен при десериализации с .NET XmlSerializer - Stack Overflow
  6. Проблема с пространством имен в XmlSerializer - Microsoft Q&A

Заключение

Правильная десериализация XML с несколькими пространствами имен в C# требует тщательной настройки атрибутов XmlSerializer для точного соответствия структуре XML. Ключевые решения:

  1. Создавайте правильные классы-контейнеры, которые соответствуют иерархии XML, включая как корневой элемент, так и вложенные элементы с их соответствующими пространствами имен.

  2. Используйте атрибуты XmlElement с явными объявлениями пространств имен для каждого XML-элемента, который принадлежит другому пространству имен.

  3. Рассмотрите техники пользовательской обработки пространств имен, такие как IgnoreNamespaceXmlTextReader, для непредсказуемых XML-структур.

  4. Реализуйте полные классы CAP alert с правильным сопоставлением пространств имен для всех элементов в протоколе экстренных оповещений.

Следуя этим подходам и обеспечивая точное соответствие ваших классов C# структуре XML с правильными объявлениями пространств имен, вы успешно десериализуете сложные XML-документы с несколькими пространствами имен в C#.