2

I need to support XML deserialization of two very similar, yet different xml files.

File 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>

File 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>

Class Definition:

<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

Files matching File1 fail while files matching File2 succeed. The key difference is the trailing slash two of the namespace definitions.

Code Sample:

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)

Error from File1:

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

Already tried: How do I specify XML serialization attributes to support namespace prefixes during deserialization in .NET?

These files come from a third-party so I cannot change the serialization process. Is there a way I can support both xml formats? Do I need two separate classes?

Thanks!

2
  • I don't actually see a difference between the two XML samples. What do you believe is the difference? Commented Jul 12, 2010 at 3:14
  • 1
    "The key difference is the trailing slash on two of the namespace definitions." Ex: xmlns:lr="x.org/schemas/LogRecord" vs. xmlns:lr="x.org/schemas/LogRecord" Commented Jul 12, 2010 at 21:46

3 Answers 3

2

Two separate class models is an option, although admittedly not a very good one.

Do you actually get these files as XML that you then process in code, or are you using smoe sort of proxy that's supposed to auto-deserialize the XML?

If you're doing your own deserialization, I would consider creating a simple XSL Transform to convert the namespaces in-memory before executing the deserialization. A transform to simply replace one pair of namespaces with another pair will be pretty short and simple.

Sign up to request clarification or add additional context in comments.

2 Comments

According to the LogRecord.xsd of the S430-4-2008 standard, the namespace is http://www.smpte-ra.org/schemas/430-4/2008/LogRecord without the trailing slash. It seems that the File1 is incorrect. To solve the problem, do a text find & replace, and correct the namespace (in-memory or on-disk). You should also contact the supplier of the File1 to correct the namespace. There is a standard, so they should follow it.
Aargh it seems that the S430-4-2008.pdf actually lists the namespace as being with a trailing slash. So File1 is ... uh ... also correct? xsd and all sample files do not have a trailing slash
1

Got around to attempt another fix for this, as I did not like having to do a find & replace inside the xml file.

So here it is --works on my machine--

I first generated the logRecordType and logRecordHeaderType classes using xsd.exe. Note that I only need to parse the logRecordHeader elements, so there is some extra work here. But the fix basically is to use the FixupNameTable so that every occurrence of the namespace with trailing slash is replaced with the one without it.

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

Comments

0

If the namespaces are different, then the XML is different.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.