Расхождения в десериализации допустимых XML-файлов с помощью System.Xml.Serialization.XmlSerializer

Довольно сложный вопрос, так что заранее спасибо. Следующие два xml-файла проверяются на соответствие заданным схемам, но при попытке десериализации с использованием .Net XmlSerializer только первый делает это правильно:

<ex:iso_10303_28 xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.iai-tech.org/ifcXML/IFC2x3/FINAL" version="2.0" xmlns:ex="urn:iso.org:standard:10303:part(28):version(2):xmlschema:common">
  <ex:iso_10303_28_header>
    <ex:name>An Example</ex:name>
    <ex:time_stamp>2010-11-12T13:04:00</ex:time_stamp>
    <ex:author>John Hancock</ex:author>
    <ex:organization>MegaCorp</ex:organization>
    <ex:preprocessor_version>a preprocessor</ex:preprocessor_version>
    <ex:originating_system>IfcXml dotNet Library</ex:originating_system>
    <ex:authorization>none</ex:authorization>
    <ex:documentation>documentation</ex:documentation>
  </ex:iso_10303_28_header>
  <ex:uos xsi:type="uos" id="uos_1" configuration="i-ifc2x3">
    <ex:Entity xsi:type="IfcOrganization" id="i1101">
      <Id xsi:nil="true" />
      <Name>MegaCorp</Name>
      <Description xsi:nil="true" />
      <Roles xsi:nil="true" />
      <Addresses xsi:nil="true" />
    </ex:Entity>
    <ex:Entity xsi:type="IfcCartesianPoint" id="i101">
      <Coordinates ex:itemType="ifc:IfcLengthMeasure" ex:cType="list">
        <IfcLengthMeasure>2500</IfcLengthMeasure>
        <IfcLengthMeasure>0</IfcLengthMeasure>
        <IfcLengthMeasure>0</IfcLengthMeasure>
      </Coordinates>
    </ex:Entity>
    <ex:Entity xsi:type="IfcDirection" id="i102">
      <DirectionRatios ex:itemType="ex:double-wrapper" ex:cType="list">
        <ex:double-wrapper>0</ex:double-wrapper>
        <ex:double-wrapper>1</ex:double-wrapper>
        <ex:double-wrapper>0</ex:double-wrapper>
      </DirectionRatios>
    </ex:Entity>
  </ex:uos>
</ex:iso_10303_28>

но второй файл не десериализуется правильно, в приведенном ниже (iso_10303_28.uos as uos1).Items десериализуется как нуль:

<?xml version="1.0" encoding="UTF-8"?>
<ex:iso_10303_28
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:ex=
    "urn:iso.org:standard:10303:part(28):version(2):xmlschema:common"
    xmlns="http://www.iai-tech.org/ifcXML/IFC2x3/FINAL"
    xsi:schemaLocation="http://www.iai-tech.org/ifcXML/IFC2x3/FINAL
    http://www.iai-tech.org/ifcXML/IFC2x3/FINAL/IFC2x3.xsd"
    version="2.0">
    <ex:iso_10303_28_header>
        <ex:name>An Example</ex:name>
        <ex:time_stamp>2010-11-12T13:04:00</ex:time_stamp>
        <ex:author>John Hancock</ex:author>
        <ex:organization>MegaCorp</ex:organization>
        <ex:preprocessor_version>a preprocessor</ex:preprocessor_version>
        <ex:originating_system>IfcXml dotNet Library</ex:originating_system>
        <ex:authorization>none</ex:authorization>
        <ex:documentation>documentation</ex:documentation>
    </ex:iso_10303_28_header>
    <uos id="uos_1" description="" configuration="i-ifc2x3" edo="">
        <IfcOrganization id="i1101">
            <Name>MegaCorp</Name>
        </IfcOrganization>
        <IfcCartesianPoint id="i101">
            <Coordinates>
                <IfcLengthMeasure>2500.0</IfcLengthMeasure>
                <IfcLengthMeasure>0.0</IfcLengthMeasure>
                <IfcLengthMeasure>0.0</IfcLengthMeasure>
            </Coordinates>
        </IfcCartesianPoint>
        <IfcDirection id="i102">
            <DirectionRatios>
                <ex:double-wrapper>0.</ex:double-wrapper>
                <ex:double-wrapper>1.</ex:double-wrapper>
                <ex:double-wrapper>0.</ex:double-wrapper>
            </DirectionRatios>
        </IfcDirection>
    </uos>
</ex:iso_10303_28>

Мой десериализатор по существу:

iso_10303_28 deserialized = (iso_10303_28)serializer.Deserialize( reader );

//using NUnit
Assert.IsNotNull(deserialized);
Assert.IsNotNull(deserialized.uos);
uos1 uos1 = deserialized.uos as uos1;
Assert.IsNotNull(uos1);
Assert.IsNotNull(uos1.Items); //<---FAILS HERE
Assert.AreEqual(3, uos1.Items.length);

Класс uos:

[System.Xml.Serialization.XmlTypeAttribute(TypeName="uos", Namespace="http://www.iai-tech.org/ifcXML/IFC2x3/FINAL")]
[System.Xml.Serialization.XmlRootAttribute("uos", Namespace="http://www.iai-tech.org/ifcXML/IFC2x3/FINAL", IsNullable=false)]
public partial class uos1 : uos {

    private Entity[] itemsField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("Entity", Namespace="urn:iso.org:standard:10303:part(28):version(2):xmlschema:common")]
    public Entity[] Items {
        get {
            return this.itemsField;
        }
        set {
            this.itemsField = value;
        }
    }
}

[System.Xml.Serialization.XmlIncludeAttribute(typeof(uos1))]    
[System.Xml.Serialization.XmlTypeAttribute( Namespace="urn:iso.org:standard:10303:part(28):version(2):xmlschema:common")]
public partial class uos {

    private string idField;

    private string[] expressField;

    private string[] configurationField;

    private string[] schemaLocationField;

    private string edoField;

    private string descriptionField;

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute(DataType="ID")]
    public string id {
        get {
            return this.idField;
        }
        set {
            this.idField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute(DataType="anyURI")]
    public string[] express {
        get {
            return this.expressField;
        }
        set {
            this.expressField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute(DataType="anyURI")]
    public string[] configuration {
        get {
            return this.configurationField;
        }
        set {
            this.configurationField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute(DataType="anyURI")]
    public string[] schemaLocation {
        get {
            return this.schemaLocationField;
        }
        set {
            this.schemaLocationField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute(DataType="anyURI")]
    public string edo {
        get {
            return this.edoField;
        }
        set {
            this.edoField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string description {
        get {
            return this.descriptionField;
        }
        set {
            this.descriptionField = value;
        }
    }
}

Класс для iso_10303_28 выглядит следующим образом:

[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:iso.org:standard:10303:part(28):version(2):xmlschema:common")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="urn:iso.org:standard:10303:part(28):version(2):xmlschema:common", IsNullable=false)]
public partial class iso_10303_28 {

    private iso_10303_28_header iso_10303_28_headerField;

    private uos uosField;

    private string versionField;

    /// <remarks/>
    public iso_10303_28_header iso_10303_28_header {
        get {
            return this.iso_10303_28_headerField;
        }
        set {
            this.iso_10303_28_headerField = value;
        }
    }

    public enum uosChoice
    {
        [XmlEnum("http://www.iai-tech.org/ifcXML/IFC2x3/FINAL:uos")]
        uos1,
        [XmlEnum("urn:iso.org:standard:10303:part(28):version(2):xmlschema:common:uos")]
        uos
    }

    [XmlIgnore()]
    public uosChoice uosChoiceField;

    [XmlChoiceIdentifier("uosChoiceField")]
    [XmlElement(ElementName = "uos", Namespace = "http://www.iai-tech.org/ifcXML/IFC2x3/FINAL", Type = typeof(uos1))]
    [XmlElement(ElementName = "uos", Namespace = "urn:iso.org:standard:10303:part(28):version(2):xmlschema:common")]
    public uos uos {
        get {
            return this.uosField;
        }
        set {
            this.uosField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string version {
        get {
            return this.versionField;
        }
        set {
            this.versionField = value;
        }
    }
}

Кто-нибудь сталкивался с этой проблемой раньше? Есть причина для этого? Я что-то пропустил? есть ли исправление или обходной путь?

Поскольку схемы и остальной код довольно велики по размеру, и попытка упростить их до минимума сбойной части вызвала больше проблем (см. этот вопрос) Я не вставлял их сюда. Однако при необходимости схемы, модульные тесты и исходный код для этой проблемы можно найти по адресу http://code.google.com/p/ifc-dotnet/


person Iain Sproat    schedule 14.11.2010    source источник


Ответы (1)


Два XML-файла не совпадают, в файле-1 uso имеет квалификацию ex, поэтому он находится в пространстве имен «urn:iso.org:standard:10303:part(28):version(2):xmlschema:common», в то время как во втором это не так и находится в пространстве имен по умолчанию.

В классе «iso_10303_28» свойство uos типа uos означает, что XmlSerializer будет ожидать элементы с именем «uos» (по умолчанию) в том же пространстве имен, что и iso_10303_28, и «xsi: type» «uos», как описано классом «uos». - под тем же - пространство имен "urn:iso.org:standard:10303:part(28):version(2):xmlschema:common". Так что это объясняет, почему вы получаете нуль во втором xml. Если у вас нет контроля над xml - и если вы сможете десериализовать любой из них, попробуйте выполнить

  1. Тип uos ничего не знает о типе uos1, если он не указан атрибутом XmlInclude. Я думаю, что вы дополнительно добавили атрибут XmlInclude в класс uos, чтобы включить uos1 в качестве известного типа. Если нет - сериализатор не будет десериализовать первый файл. Если нет - вы должны сделать это, как показано ниже

    [System.Xml.Serialization.XmlInclude(typeof(uos1))] [System.Xml.Serialization.XmlTypeAttribute(Namespace = "urn:iso.org:standard:10303:part(28):version(2):xmlschema:common ")] открытый частичный класс uos {

    }
    
  2. Вы можете добавить атрибуты XmlElement для свойства iso_10303_28.uos, чтобы принимать имена в любом из пространств имен, и использовать идентификатор выбора или использовать «uos[]» в качестве типа вместо «uos». С выбором идентификатора - это можно сделать как

[System.Xml.Serialization.XmlRootAttribute(Namespace = "urn:iso.org:standard:10303:part(28):version(2):xmlschema:common", IsNullable = false)] общедоступный частичный класс iso_10303_28 { private uos uosField ;

    public enum uosChoice
    {
        [XmlEnum("http://www.iai-tech.org/ifcXML/IFC2x3/FINAL:uos")]
        uos,
        [XmlEnum("urn:iso.org:standard:10303:part(28):version(2):xmlschema:common:uos")]
        uos1
    }

    [XmlIgnore]
    public uosChoice uosChoiceField;

    [XmlChoiceIdentifier("uosChoiceField")]
    [XmlElement(ElementName = "uos", Namespace = "http://www.iai-tech.org/ifcXML/IFC2x3/FINAL", Type = typeof(uos1))]
    [XmlElement(ElementName = "uos", Namespace = "urn:iso.org:standard:10303:part(28):version(2):xmlschema:common")]
    public uos uos
    {
        get
        {
            return this.uosField;
        }
        set
        {
            this.uosField = value;
        }
    }


}

РЕДАКТИРОВАТЬ: Чтобы правильно анализировать элементы, измените uos1 следующим образом.

 [System.Xml.Serialization.XmlTypeAttribute(TypeName = "uos", Namespace = "http://www.iai-tech.org/ifcXML/IFC2x3/FINAL")]
    [System.Xml.Serialization.XmlRootAttribute("uos", Namespace = "http://www.iai-tech.org/ifcXML/IFC2x3/FINAL", IsNullable = false)]
    public partial class uos1 : uos
    {
        private Entity[] itemsField;

        /// <remarks/>
        [XmlElement(ElementName = "Entity", Namespace = "urn:iso.org:standard:10303:part(28):version(2):xmlschema:common", Type = typeof(Entity))]
        [XmlElement(ElementName = "IfcOrganization", Namespace = "http://www.iai-tech.org/ifcXML/IFC2x3/FINAL", Type = typeof(IfcOrganization))]
        [XmlElement(ElementName = "IfcCartesianPoint", Namespace = "http://www.iai-tech.org/ifcXML/IFC2x3/FINAL", Type = typeof(IfcCartesianPoint))]
        [XmlElement(ElementName = "IfcDirection", Namespace = "http://www.iai-tech.org/ifcXML/IFC2x3/FINAL", Type = typeof(IfcDirection))]
        public Entity[] Items
        {
            get
            {
                return this.itemsField;
            }
            set
            {
                this.itemsField = value;
            }
        }

    }

 [System.Xml.Serialization.XmlTypeAttribute(TypeName = "IfcOrganization", Namespace = "http://www.iai-tech.org/ifcXML/IFC2x3/FINAL")]
    public class IfcOrganization : Entity
    {

    }

    [System.Xml.Serialization.XmlTypeAttribute(TypeName = "IfcCartesianPoint", Namespace = "http://www.iai-tech.org/ifcXML/IFC2x3/FINAL")]
    public class IfcCartesianPoint : Entity
    {

    }

    [System.Xml.Serialization.XmlTypeAttribute(TypeName = "IfcDirection", Namespace = "http://www.iai-tech.org/ifcXML/IFC2x3/FINAL")]
    public class IfcDirection : Entity
    {

    }

    [XmlInclude(typeof(IfcOrganization))]
    [XmlInclude(typeof(IfcCartesianPoint))]
    [XmlInclude(typeof(IfcDirection))]
    [System.Xml.Serialization.XmlTypeAttribute(TypeName = "Entity", Namespace = "urn:iso.org:standard:10303:part(28):version(2):xmlschema:common")]
    public class Entity
    {

    }
person Om Deshmane    schedule 18.11.2010
comment
При применении № 2 я получил пару ошибок. [XmlChoiceIdentifier("ignored")] должно быть [XmlChoiceIdentifier("uosChoice")], а public uosChoice uosChoiceField должно иметь атрибут [XmlIgnore()]. - person Iain Sproat; 18.11.2010
comment
Внося эти изменения, как указано в приведенном выше комментарии (зафиксированном как редакция 7 на code.google.com/p/ifc-dotnet/source/checkout ), тест десериализации теперь десериализует uos, поэтому он не равен нулю. К сожалению, тогда он не может преобразовать uos в uos1, что означает, что все важные uos1.Items по-прежнему недоступны. :( - person Iain Sproat; 18.11.2010
comment
Извините, [XmlChoiceIdentifier (игнорируется)] следует изменить на [XmlChoiceIdentifier (uosChoice)], как указано - person Om Deshmane; 19.11.2010
comment
такое изменение атрибута XmlElement будет корректно анализироваться в uos1 [XmlElement(ElementName = uos, Namespace = iai -tech.org/ifcXML/IFC2x3/FINAL, Type = typeof(uos1))] - person Om Deshmane; 19.11.2010
comment
Я отредактировал сообщение, чтобы отразить - добавление типа в XmlElement - это сработает для вас - person Om Deshmane; 19.11.2010
comment
@mho Спасибо, я внес это изменение, и теперь он может преобразовывать uos в uos1. К сожалению, теперь он утверждает, что uos1.items имеет значение null! Любые идеи о том, как решить эту проблему? Я пробовал несколько вещей, но пока безуспешно. - person Iain Sproat; 19.11.2010
comment
Сделайте то, что не работает, больше похожим на то, что работает, и вдруг оно будет работать лучше. - person John Saunders; 19.11.2010
comment
@John Saunders Шаг предварительной десериализации, который открывает файл xml и заменяет <uos на <ex:uos xsi:type="uos", а для каждого дочернего элемента uos заменяет <IfcSomeName на <ex:Entity xsi:type="IfcSomeName", будет работать (я проверял, это работает), но это похоже на неприятный взлом. Я очень за то, чтобы найти что-то более элегантное. - person Iain Sproat; 19.11.2010
comment
@sprocket: это это неприятный хак - не делайте этого. Почему два файла не совпадают? Кто не работает - тот не прав! - person John Saunders; 19.11.2010
comment
@John Saunders Я согласен, очень неправильно! К сожалению, это формат, в котором представлены примеры данных от органа, разрабатывающего стандарты IFC. Вполне вероятно, что любое программное обеспечение, которое желает быть проверенным как «совместимое с IFC», будет проверено на соответствие «неправильному» xml или оба. Дополнительную информацию можно найти на buildingsmart.com/bim iai-tech.org/products/ifc_specification/ifcxml-releases/summary - person Iain Sproat; 19.11.2010
comment
@sprocket: неправильно неправильно. Существует только один набор стандартов XML, а не по одному для каждого органа. Эксперты должны поправлять власти, когда они ошибаются. Эти элементы должны находиться в одном пространстве имен, а не в правильном пространстве имен или ни в одном пространстве имен. Период. Ваша обязанность как профессионала состоит в том, чтобы тщательно указать, почему они ошибаются и что им нужно сделать, чтобы исправить это, возможно, заручившись помощью других людей в вашем положении, чтобы сделать это. - person John Saunders; 19.11.2010
comment
на самом деле бит <uos менять не нужно, только его дочерние элементы: замена <IfcSomeName на <ex:Entity xsi:type="IfcSomeName". - person Iain Sproat; 19.11.2010
comment
@sprocket: вы не найдете в Интернете слишком много помощи о том, как работать с пятью различными недопустимыми форматами, которые притворяются, что они XML, но не являются таковыми. - person John Saunders; 19.11.2010
comment
@sprocket: Я надеюсь, вы понимаете, что я имею в виду, что вы должны 1) Убедить себя, что они неправы, затем 2) Написать что-нибудь, чтобы убедить их, что они неправы. Кто знает? У них действительно может быть ошибка в коде. - person John Saunders; 19.11.2010
comment
@sprocket: uos1.Items имеет значение null, потому что в xml нет элемента с именем Entity под ним. Чтобы заставить его работать, вам нужно добавить все имена в качестве атрибутов XmlElement в поле Items (это печально, но логика работы XmlSerializer имеет это ограничение). - person Om Deshmane; 21.11.2010
comment
Однако в этом случае вам не нужно использовать какие-либо идентификаторы выбора (поскольку свойство question представляет собой массив). Просто продолжайте добавлять такие XmlElements, как /// ‹remarks/› [System.Xml.Serialization.XmlElementAttribute(IfcDirection, Namespace=iai-tech.org/ifcXML/IFC2x3/FINAL)] [System.Xml.Serialization.XmlElementAttribute(Entity, Namespace=urn:iso.org:standard:10303:part( 28): версия (2): xmlschema: common)] public Entity [] Items { get { return this.itemsField; } установить { this.itemsField = значение; } } - person Om Deshmane; 21.11.2010
comment
Или, если в данный момент вас не волнует содержимое Xml, вы можете вместо этого использовать атрибут XmlAnyType и изменить Entity[] на XmlElement[] - person Om Deshmane; 21.11.2010
comment
@mho Добавление XmlElements, как вы описываете, приводит к следующей ошибке: SetUp : System.InvalidOperationException : There was an error reflecting type 'IfcDotNet.iso_10303_28'. ----> System.InvalidOperationException : There was an error reflecting property 'uos'. ----> System.InvalidOperationException : There was an error reflecting type 'IfcDotNet.uos1'. ----> System.InvalidOperationException : There was an error reflecting property 'Items'. ----> System.InvalidOperationException : You need to add XmlChoiceIdentifierAttribute to the 'Items' member. - TestIfcXmlSerializer.cs:61 - person Iain Sproat; 21.11.2010
comment
О, исправление или дополнение: вам нужно использовать разные типы для каждого из Entity, IfcOrganization и т. д. Я отредактировал сообщение, чтобы отразить правильный способ сделать это. - person Om Deshmane; 22.11.2010
comment
@mho Это работает отлично! Спасибо за вашу выдающуюся помощь в этом вопросе. :) - person Iain Sproat; 22.11.2010