.Net Xml десериализация - поддержка нескольких пространств имен - PullRequest
2 голосов
/ 24 июня 2010

Мне нужно поддерживать десериализацию XML двух очень похожих, но разных XML-файлов.

Файл 1:

<?xml version="1.0" encoding="UTF-8"?>
<lr:LogReport xmlns:dcml="http://www.x1.org/schemas/Types/"
    xmlns:ds="http://www.x3.org/2000/09/xmldsig#"
    xmlns:lr="http://www.x.org/schemas/LogRecord/"
    xmlns:xs="http://www.x3.org/2001/XMLSchema"
    xmlns:xsi="http://www.x3.org/2001/XMLSchema-instance">
    <lr:reportDate>2010-03-05T07:00:52-08:00</lr:reportDate>

Файл 2:

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<LogReport xmlns="http://www.x.org/schemas/LogRecord" 
    xmlns:dcml="http://www.x1.org/schemas/Types" 
    xmlns:ds="http://www.x3.org/2000/09/xmldsig#" 
    xmlns:lr="http://www.x.org/schemas/LogRecord" 
    xmlns:xs="http://www.x3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.x3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.x.org/schemas/LogRecord ./LogRecord.xsd  http://www.x1.org/schemas/Types ./Types.xsd">
    <lr:reportDate>2010-02-26T07:00:02-08:00</lr:reportDate>

Определение класса:

<System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.42"), _
 System.SerializableAttribute(), _
 System.Diagnostics.DebuggerStepThroughAttribute(), _
 System.ComponentModel.DesignerCategoryAttribute("code"), _
 System.Xml.Serialization.XmlTypeAttribute([Namespace]:="http://www.smpte-ra.org/schemas/430-4/2008/LogRecord"), _
 System.Xml.Serialization.XmlRootAttribute("LogReport", [Namespace]:="http://www.smpte-ra.org/schemas/430-4/2008/LogRecord", IsNullable:=False)> _
Partial Public Class LogReport

Файлы, соответствующие File1, завершаются ошибкой, а файлы, соответствующие File2, выполняются успешно. Ключевым отличием является конечная косая черта двух определений пространства имен.

Пример кода:

Dim oLogReport As New LogReport
Dim oType As System.Type = oLogReport.GetType
Dim oReader As System.Xml.XmlReader = Nothing
Dim oSerializer As New XmlSerializer(oType)
oReader = System.Xml.XmlReader.Create(sFileName)
oLogReport = CType(oSerializer.Deserialize(oReader), LogReport)

Ошибка из File1:

{"<LogReport xmlns='http://www.x.org/schemas/LogRecord/'> was not expected."}

Уже пробовал: Как указать атрибуты сериализации XML для поддержки префиксов пространства имен при десериализации в .NET?

Эти файлы принадлежат сторонним производителям, поэтому я не могу изменить процесс сериализации. Есть ли способ, которым я могу поддерживать оба формата XML? Нужны ли мне два отдельных класса?

Спасибо!

Ответы [ 3 ]

2 голосов
/ 24 июня 2010

Две отдельные модели класса - вариант, хотя, правда, не очень хороший.

Получаете ли вы эти файлы в виде XML, а затем обрабатываете их в коде или используете прокси-сервер более плавного типа, который должен автоматически десериализовать XML?

Если вы выполняете собственную десериализацию, я бы рассмотрел создание простого XSL-преобразования для преобразования пространств имен в памяти перед выполнением десериализации. Преобразование для простой замены одной пары пространств имен другой парой будет довольно коротким и простым.

1 голос
/ 30 марта 2012

Попытался найти другое исправление для этого, так как мне не нравилось делать поиск и замену в файле XML.

Итак, вот оно - работает на моей машине -

Сначала я сгенерировал классы logRecordType и logRecordHeaderType, используя xsd.exe .Обратите внимание, что мне нужно только разобрать элементы logRecordHeader, поэтому здесь есть дополнительная работа.Но исправление в основном состоит в том, чтобы использовать FixupNameTable, чтобы каждое вхождение пространства имен с конечной косой чертой заменялось на другое без него.

namespace DolbyService.Schemas
{
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

public partial class logRecordType
{
    public const string xmlns_lr = "http://www.smpte-ra.org/schemas/430-4/2008/LogRecord";
    public const string xmlns_dcml = "http://www.smpte-ra.org/schemas/433/2008/dcmlTypes";


    public static IEnumerable<logRecordHeaderType> Parse(Stream xmlfile)
    {
        XmlReaderSettings settings = new XmlReaderSettings();
        settings.NameTable = new FixupNameTable();
        using (var rdr = XmlReader.Create(xmlfile, settings))
        {
            var headerSerialize = new XmlSerializer(typeof(logRecordHeaderType));

            while (rdr.ReadToFollowing(logRecordHeaderType.LogRecordHeader, logRecordType.xmlns_lr))
            {
                using (var subreader = rdr.ReadSubtree())
                {
                    var ok = headerSerialize.CanDeserialize(subreader);
                    var logRecordHeader = headerSerialize.Deserialize(subreader);
                    yield return logRecordHeader as logRecordHeaderType;
                }
            }
        }
    }
}

internal class FixupNameTable : XmlNameTable
{
    private NameTable _table = new NameTable();

    public override string Add(string key)
    {
        key = ReplaceKey(key);
        return _table.Add(key);
    }

    private static string ReplaceKey(string key)
    {
        if (key.StartsWith(logRecordType.xmlns_lr)) key = logRecordType.xmlns_lr;
        if (key.StartsWith(logRecordType.xmlns_dcml)) key = logRecordType.xmlns_dcml;
        return key;
    }

    public override string Add(char[] array, int offset, int length)
    {
        return _table.Add(array, offset, length);
    }

    public override string Get(string key)
    {
        key = ReplaceKey(key);
        return _table.Get(key);
    }

    public override string Get(char[] array, int offset, int length)
    {
        return _table.Get(array, offset, length);
    }
}

[XmlRoot(logRecordHeaderType.LogRecordHeader, Namespace = logRecordType.xmlns_lr, IsNullable = false)]
public partial class logRecordHeaderType
{
    public const string LogRecordHeader = "LogRecordHeader";

    private static XmlSerializer _ser = new XmlSerializer(typeof(logRecordHeaderType));

    public static logRecordHeaderType Deserialize(XmlReader xmlReader)
    {
        return _ser.Deserialize(xmlReader) as logRecordHeaderType;
    }

    public static bool CanDeserialize(XmlReader xmlReader)
    {
        return _ser.CanDeserialize(xmlReader);
    }
}

public class HeaderComparer : IEqualityComparer<logRecordHeaderType>
{
    public bool Equals(logRecordHeaderType x, logRecordHeaderType y)
    {
        return x.EventID == y.EventID;
    }

    public int GetHashCode(logRecordHeaderType obj)
    {
        return obj.EventID.GetHashCode();
    }
}
}
0 голосов
/ 24 июня 2010

Если пространства имен разные, то XML другой.

...