2

A fairly involved question, so thanks in advance. The following two xml files both validate against the given schemas, but on attempting to deserialize using .Net's XmlSerializer only the first does so correctly:

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

but the second file does not deserialize correctly, in the below (iso_10303_28.uos as uos1).Items is deserialized as null:

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

My deserializer is essentially:

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

The uos class is:

[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;
        }
    }
}

The class for iso_10303_28 is as follows:

[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;
        }
    }
}

Has anyone come across this issue before? Is there a reason for this? Have I missed something? is there a fix or workaround?

As the schemas and rest of the code is fairly large in size, and trying to simplify them down to the minimum failing part caused more problems (see this question) I've not pasted them here. However, if required, schemas, unit tests and source for this issue can be found at http://code.google.com/p/ifc-dotnet/

Community
  • 1
  • 1
Iain Sproat
  • 5,210
  • 11
  • 48
  • 68

1 Answers1

3

The two XML files aren't the same, in file-1 uso is qualified under ex so it is under "urn:iso.org:standard:10303:part(28):version(2):xmlschema:common" namespace, while in second it is not and is under default namespace.

In class 'iso_10303_28' property uos of type uos which means that XmlSerializer will expect elements with name 'uos' (by default) under the same namespace as that of iso_10303_28 and 'xsi:type' 'uos' as described by class 'uos'- under the same - "urn:iso.org:standard:10303:part(28):version(2):xmlschema:common" namespace. So this explains why you get null in second xml. If you have no control over xml - and should you be able to deserialize either of them, try following

  1. Type uos has no knowledge of type uos1 unless specified by XmlInclude attribute. I think what you've additionally added an XmlInclude attribute on uos class to include uos1 as a known type. If not - the serializer won't de-serialize first file. If not - you should do that as below

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

    }
    
  2. You can add XmlElement attributes for property iso_10303_28.uos to accept names under either namespace - and use a choice identifier or use 'uos[]' as type instead of 'uos'. With choice identifier - it can be done as

[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 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;
        }
    }


}

EDIT: To parse Items correctly, modify uos1 like this

 [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
    {

    }
Om Deshmane
  • 808
  • 6
  • 11
  • On applying #2, I got a couple of errors. `[XmlChoiceIdentifier("ignored")]` should be `[XmlChoiceIdentifier("uosChoice")]` and `public uosChoice uosChoiceField` should have the `[XmlIgnore()]` attribute on it. – Iain Sproat Nov 18 '10 at 14:13
  • Making these changes as noted in the above comment (committed as Revision 7 to code.google.com/p/ifc-dotnet/source/checkout ), the deserialization test now deserializes uos, so it is not null. Unfortunately, it then cannot cast uos to uos1 which means the all important uos1.Items is still not accessible. :( – Iain Sproat Nov 18 '10 at 16:34
  • Sorry, [XmlChoiceIdentifier("ignored")] should be changed to [XmlChoiceIdentifier("uosChoice")] as pointed out – Om Deshmane Nov 19 '10 at 05:30
  • changing XmlElement attribute like this will correctly parse into uos1 [XmlElement(ElementName = "uos", Namespace = "http://www.iai-tech.org/ifcXML/IFC2x3/FINAL", Type = typeof(uos1))] – Om Deshmane Nov 19 '10 at 05:37
  • I have edited the post to reflect - addition of Type in XmlElement - it will work for you – Om Deshmane Nov 19 '10 at 05:39
  • @mho Thanks, I made that change and it now can cast uos to uos1. Unfortunately, it now claims uos1.items is null! Any ideas as to how to resolve this? I've tried a few things but with no luck yet. – Iain Sproat Nov 19 '10 at 12:37
  • Make the one that doesn't work be more like the one that works, and suddenly it will work better. – John Saunders Nov 19 '10 at 16:58
  • @John Saunders A pre-deserialization step which opens the xml file and replaces ` – Iain Sproat Nov 19 '10 at 17:36
  • @sprocket: it _is_ a nasty hack - don't do it. Why are the two files not the same? The one that doesn't work - is wrong! – John Saunders Nov 19 '10 at 18:11
  • @John Saunders I agree, very wrong! Unfortunately, that's the format in which the example data from the authority which creates the IFC standards is provided in. It is likely that any software which wishes to be verified as 'IFC-compliant' will be tested against the 'wrong' xml, or both. Further reading at http://www.buildingsmart.com/bim http://www.iai-tech.org/products/ifc_specification/ifcxml-releases/summary – Iain Sproat Nov 19 '10 at 18:19
  • @sprocket: Wrong is wrong. There is only one set of XML standards, not one per "authority". It is up to the experts to correct the "authorities" when they are wrong. Those elements must be in a single namespace, not "either the correct namespace or none". Period. Your responsibility as a professional is to carefully point out why they are wrong and what they need to do to fix it, possibly enlisting the help of others in your position to get it done. – John Saunders Nov 19 '10 at 18:31
  • actually, the ` – Iain Sproat Nov 19 '10 at 18:31
  • @sprocket: you're not going to find too much help on the Internet on "how to work with five different invalid formats that pretend they're XML but aren't". – John Saunders Nov 19 '10 at 18:32
  • @sprocket: I hope you realize I mean you should 1) Convince yourself they're wrong, then 2) Write something up to convince them they're wrong. Who knows? They may actually have a bug in their code. – John Saunders Nov 19 '10 at 18:39
  • @sprocket: uos1.Items is null because there's no element in the xml with name Entity under it. To make it work, you'd have to add all the names as XmlElement attributes over the Items field (this is unfortunate but the logic over XmlSerializer works has this limitation). – Om Deshmane Nov 21 '10 at 16:35
  • However, you don't have to use any choice identifiers in this case (since the property is question is an array). Just keep adding those XmlElements like /// [System.Xml.Serialization.XmlElementAttribute("IfcDirection", Namespace="http://www.iai-tech.org/ifcXML/IFC2x3/FINAL")] [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; } } – Om Deshmane Nov 21 '10 at 16:39
  • Or, if you don't care about contents of the Xml at this point in time - you may use XmlAnyType attribute instead and change Entity[] to XmlElement[] – Om Deshmane Nov 21 '10 at 16:42
  • @mho Adding XmlElements as you describe results in the following error: `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` – Iain Sproat Nov 21 '10 at 18:27
  • Oh, correction or addition : you need to use different types for each of Entity, IfcOrganization etc. I've edited the post to reflect correct way of doing this – Om Deshmane Nov 22 '10 at 02:06
  • @mho That works perfectly! Thank you for your outstanding help on this question. :) – Iain Sproat Nov 22 '10 at 16:40