h__
h__

Reputation: 793

Deserialization to classes created from XSD does not contain all data

I wanted to deserialize TCX files containing sport data into some my local/temporary object. I used XSD schema describing such format and created classes using xsd2code.

After that I was able to deserialize XML into proper object and the deserialzator didn't throw any exception. But when I started to expand behaviour of component I noticed that some fields are not properly fullfield.

The problem is about reading the tags Track. Deserialized Lap in substructure shows all data properly, but the Tracks number is displayed as 0.

Is there any way to trigger deserializer to interpret it properly? Did I miss some attribute? Why it is not reading it properly even if there was no exception.

I know, it is hard to explain and put all needed classes into comment - so I share my current status here and just write that: GetUnifiedData_WhenTrackPointsAreGivenInTcx_UnifiedTrackPointShouldRetreiveIt() is not passing and shows my problem.

<Activities>
 <Activity Sport="Running">
  <Id>2015-01-25T12:14:34Z</Id>
  <Lap StartTime="2015-01-25T12:14:34Z">
    <TotalTimeSeconds>507.0989990</TotalTimeSeconds>
    <DistanceMeters>1000.0000000</DistanceMeters>
    <MaximumSpeed>2.5790000</MaximumSpeed>
    <Calories>95</Calories>
    <AverageHeartRateBpm xsi:type="HeartRateInBeatsPerMinute_t">
      <Value>155</Value>
    </AverageHeartRateBpm>
    <MaximumHeartRateBpm xsi:type="HeartRateInBeatsPerMinute_t">
      <Value>173</Value>
    </MaximumHeartRateBpm>
    <Intensity>Resting</Intensity>
    <TriggerMethod>Distance</TriggerMethod>
    <Track>
      <Trackpoint>
        <Time>2015-01-25T12:14:34Z</Time>
        <Position>
          <LatitudeDegrees>50.8918607</LatitudeDegrees>
          <LongitudeDegrees>16.7403161</LongitudeDegrees>
        </Position>
        <AltitudeMeters>233.1999969</AltitudeMeters>
        <DistanceMeters>0.0000000</DistanceMeters>
        <HeartRateBpm xsi:type="HeartRateInBeatsPerMinute_t">
          <Value>88</Value>
        </HeartRateBpm>
        <Extensions>
          <TPX xmlns="http://www.garmin.com/xmlschemas/ActivityExtension/v2" CadenceSensor="Footpod">
            <Speed>0.0000000</Speed>
          </TPX>
        </Extensions>
      </Trackpoint>
    </Track>
    <Track>
    </Track>
    <Extensions>
      <FatCalories xmlns="http://www.garmin.com/xmlschemas/FatCalories/v1">
        <Value>0</Value>
      </FatCalories>
      <LX xmlns="http://www.garmin.com/xmlschemas/ActivityExtension/v2">
        <AvgSpeed>1.9720000</AvgSpeed>
      </LX>
    </Extensions>
  </Lap>
</Activity>
</Activities>

Upvotes: 0

Views: 434

Answers (1)

dbc
dbc

Reputation: 116526

The problem is that your data model is wrong. In the below excerpt, you will see two Track elements below the Lap element:

<?xml version="1.0" encoding="utf-16" standalone="no"?>
<TrainingCenterDatabase xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.garmin.com/xmlschemas/ActivityExtension/v2 http://www.garmin.com/xmlschemas/ActivityExtensionv2.xsd http://www.garmin.com/xmlschemas/FatCalories/v1 http://www.garmin.com/xmlschemas/fatcalorieextensionv1.xsd http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd">
  <Activities>
    <Activity Sport="Running">
      <Id>2015-01-25T12:14:34Z</Id>
      <Lap StartTime="2015-01-25T12:14:34Z">
        <Track>
          <Trackpoint>
            <Time>2015-01-25T12:14:34Z</Time>
            ... etc etc
          </Trackpoint>
          <Trackpoint>
            ... etc etc
          </Trackpoint>
        </Track>
        <Track>
          <Trackpoint>
            ... etc etc
          <Trackpoint>
        </Track>

However, in your data model Track is a singleton property of Lap containing an array of TrackPoint_t entries. When XmlSerializer encounters more than one Track elements it fails to deserialize them.

Thus your data model needs to be as follows. Introduce an intermediate class Track_t:

[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2")]
public class Track_t
{
    public Track_t()
    {
        this.Track = new List<Trackpoint_t>();
    }

    [System.Xml.Serialization.XmlElement("TrackPoint", typeof(Trackpoint_t), IsNullable = false)]
    public List<Trackpoint_t> Track { get; set; }
}

And modify ActivityLap_t as follows:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.34234")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2")
]
public partial class ActivityLap_t
{
    private List<Track_t> _track;  // Change type to `Track_t`

    public ActivityLap_t()
    {
        this._extensions = new Extensions_t();
        this._track = new List<Track_t>(); // Change type to `Track_t`
        this._maximumHeartRateBpm = new HeartRateInBeatsPerMinute_t();
        this._averageHeartRateBpm = new HeartRateInBeatsPerMinute_t();
    }

    // Change type to `Track_t` and change attribute to `XmlElement`
    [System.Xml.Serialization.XmlElement("Track", typeof(Track_t), IsNullable = false)]
    public List<Track_t> Track
    {
        get { return this._track; }
        set { this._track = value; }
    }

    // Remainder unchanged.
}

You will also need to modify Course_t in the same way.

The xsd does specify the existence of a type Track_t and that ActivityLap_t can contain an unbounded number of Track elements of this type, so somehow the XSD was transformed into classes incorrectly:

  <xsd:complexType name="ActivityLap_t">
    <xsd:sequence>
      <xsd:element name="TotalTimeSeconds" type="xsd:double" />
      <xsd:element name="DistanceMeters" type="xsd:double" />
      <xsd:element name="MaximumSpeed" type="xsd:double" minOccurs="0" />
      <xsd:element name="Calories" type="xsd:unsignedShort" />
      <xsd:element name="AverageHeartRateBpm" type="HeartRateInBeatsPerMinute_t" minOccurs="0" />
      <xsd:element name="MaximumHeartRateBpm" type="HeartRateInBeatsPerMinute_t" minOccurs="0" />
      <xsd:element name="Intensity" type="Intensity_t" />
      <xsd:element name="Cadence" type="CadenceValue_t" minOccurs="0" />
      <xsd:element name="TriggerMethod" type="TriggerMethod_t" />
      <xsd:element name="Track" type="Track_t" minOccurs="0" maxOccurs="unbounded" />  <!-- Notice that maxOccurs is unbounded so "Track" needs to be a list  -->
      <xsd:element name="Notes" type="xsd:string" minOccurs="0" />
      <xsd:element name="Extensions" type="Extensions_t" minOccurs="0">
        <xsd:annotation>
          <xsd:documentation>
            You can extend Training Center by adding your own elements from another schema here.
          </xsd:documentation>
        </xsd:annotation>
      </xsd:element>
    </xsd:sequence>
    <xsd:attribute name="StartTime" type="xsd:dateTime" use="required" />
  </xsd:complexType>

and

  <xsd:complexType name="Track_t">
    <xsd:sequence>
      <xsd:element name="TrackPoint" type="TrackPoint_t" maxOccurs="unbounded" />
    </xsd:sequence>
  </xsd:complexType>

Upvotes: 1

Related Questions