0

I am trying to parse the following XML string into an object. it can either be a "UpdateStructure" or "AddProcessLog". Based on the type I want to parse it into different objects.

Here is an XML sample showing AddProcessLog:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Envelope>
  <Header>
    <Sender>MCTC</Sender>
    <MacAddress>00-0C-C6-86-E4-B2</MacAddress>
  </Header>
  <Body>
    <m:AddProcessLog xmlns:m="urn:mcsmart:machine" Version="2.1.0">
      <Machine Name="MC-TC Nick" Timestamp="1683621447526">
        <Active>false</Active>
        <ActLineSpeed>0.000</ActLineSpeed>
        <ActProductWeight>0.000</ActProductWeight>
        <EventCodes />
        <HasLineControl>false</HasLineControl>
        <InputType>Relay</InputType>
        <MaxEventType>None</MaxEventType>
        <OrderNumber />
        <ProductionMode>Extrusion</ProductionMode>
        <Recipe />
        <SetLineCapacity>-1.000</SetLineCapacity>
        <SetLineSpeed>0.125</SetLineSpeed>
        <SetProductWeight>1000.000</SetProductWeight>
        <ActTotalExtruderCapacity>0.000</ActTotalExtruderCapacity>
        <SetTotalExtruderCapacity>0.000</SetTotalExtruderCapacity>
        <Group Index="0" Name="Group1">
          <ActDosingTime>0.100</ActDosingTime>
          <SetDosingTime>0.100</SetDosingTime>
          <ActTachoVoltage>0.078</ActTachoVoltage>
          <SetTachoVoltage>24.000</SetTachoVoltage>
          <ShotWeight>0.100</ShotWeight>
          <ActExtruderCapacity>0.000</ActExtruderCapacity>
          <SetExtruderCapacity>0.028</SetExtruderCapacity>
          <ActExtruderSpeed>0.000</ActExtruderSpeed>
          <SetExtruderSpeed>0.000</SetExtruderSpeed>
          <ActWeightCapacity>0.000</ActWeightCapacity>
          <Consumption>0.000</Consumption>
          <EventCodes />
          <MaxEventType>None</MaxEventType>
          <Unit Index="0" Name="Unit1" Type="MCPowder">
            <ActMotorSpeed>0.000</ActMotorSpeed>
            <BatchCounter>0</BatchCounter>
            <BatchWeight>0.000</BatchWeight>
            <DosingMode>Gravimetric</DosingMode>
            <DosingTool>SP15</DosingTool>
            <EventCodes />
            <GrossWeight>0.000</GrossWeight>
            <MaxEventType>None</MaxEventType>
            <SetMotorSpeed>55.000</SetMotorSpeed>
            <Status>Off</Status>
            <Component Index="0" Name="Component1" Type="AdditiveV">
              <ActCapacity>0.000</ActCapacity>
              <ActDosingPercentage>0.000</ActDosingPercentage>
              <BandNumber>0</BandNumber>
              <Consumption>0.000</Consumption>
              <Material>sp15-ng</Material>
              <CorrectionFactor>1.000</CorrectionFactor>
              <EventCodes />
              <GranulateType>Normal</GranulateType>
              <MeasurementTime>0.000</MeasurementTime>
              <ActDosingWeight>0.000</ActDosingWeight>
              <SetDosingWeight>0.000</SetDosingWeight>
              <MaxEventType>None</MaxEventType>
              <PumpType>None</PumpType>
              <PumpSize>None</PumpSize>
              <RegrindFillLevel>0.000</RegrindFillLevel>
              <RegrindPercentage>0.000</RegrindPercentage>
              <SetCapacity>0.000</SetCapacity>
              <SetDosingPercentage>0.000</SetDosingPercentage>
              <Tolerance>25.000</Tolerance>
              <ValveType>None</ValveType>
            </Component>
          </Unit>
          <Unit Index="1" Name="Unit2" Type="MCBalance">
            <ActMotorSpeed>0.000</ActMotorSpeed>
            <BatchCounter>0</BatchCounter>
            <BatchWeight>0.000</BatchWeight>
            <DosingMode>Gravimetric</DosingMode>
            <DosingTool>GLX</DosingTool>
            <EventCodes />
            <GrossWeight>0.000</GrossWeight>
            <MaxEventType>None</MaxEventType>
            <SetMotorSpeed>0.000</SetMotorSpeed>
            <Status>Off</Status>
            <Component Index="0" Name="Component2" Type="AdditiveV">
              <ActCapacity>0.000</ActCapacity>
              <ActDosingPercentage>0.000</ActDosingPercentage>
              <BandNumber>0</BandNumber>
              <Consumption>0.000</Consumption>
              <Material>glx-ng</Material>
              <CorrectionFactor>1.000</CorrectionFactor>
              <EventCodes />
              <GranulateType>Normal</GranulateType>
              <MeasurementTime>432.900</MeasurementTime>
              <ActDosingWeight>0.000</ActDosingWeight>
              <SetDosingWeight>0.000</SetDosingWeight>
              <MaxEventType>None</MaxEventType>
              <PumpType>None</PumpType>
              <PumpSize>None</PumpSize>
              <RegrindFillLevel>0.000</RegrindFillLevel>
              <RegrindPercentage>0.000</RegrindPercentage>
              <SetCapacity>0.015</SetCapacity>
              <SetDosingPercentage>55.000</SetDosingPercentage>
              <Tolerance>25.000</Tolerance>
              <ValveType>None</ValveType>
            </Component>
          </Unit>
        </Group>
        <Group Index="1" Name="Group2">
          <ActDosingTime>0.100</ActDosingTime>
          <SetDosingTime>0.100</SetDosingTime>
          <ActTachoVoltage>0.000</ActTachoVoltage>
          <SetTachoVoltage>24.000</SetTachoVoltage>
          <ShotWeight>0.100</ShotWeight>
          <ActExtruderCapacity>0.000</ActExtruderCapacity>
          <SetExtruderCapacity>0.028</SetExtruderCapacity>
          <ActExtruderSpeed>0.000</ActExtruderSpeed>
          <SetExtruderSpeed>0.000</SetExtruderSpeed>
          <ActWeightCapacity>0.000</ActWeightCapacity>
          <Consumption>0.000</Consumption>
          <EventCodes />
          <MaxEventType>None</MaxEventType>
          <Unit Index="0" Name="Unit1" Type="MCBalance">
            <ActMotorSpeed>0.000</ActMotorSpeed>
            <BatchCounter>0</BatchCounter>
            <BatchWeight>0.000</BatchWeight>
            <DosingMode>Volumetric</DosingMode>
            <DosingTool>GLX</DosingTool>
            <EventCodes />
            <GrossWeight>0.000</GrossWeight>
            <MaxEventType>None</MaxEventType>
            <SetMotorSpeed>5.000</SetMotorSpeed>
            <Status>Off</Status>
            <Component Index="0" Name="Component1" Type="AdditiveV">
              <ActCapacity>0.000</ActCapacity>
              <ActDosingPercentage>0.000</ActDosingPercentage>
              <BandNumber>0</BandNumber>
              <Consumption>0.000</Consumption>
              <Material>glx-ng</Material>
              <CorrectionFactor>1.000</CorrectionFactor>
              <EventCodes />
              <GranulateType>Normal</GranulateType>
              <MeasurementTime>0.000</MeasurementTime>
              <ActDosingWeight>0.000</ActDosingWeight>
              <SetDosingWeight>0.000</SetDosingWeight>
              <MaxEventType>None</MaxEventType>
              <PumpType>None</PumpType>
              <PumpSize>None</PumpSize>
              <RegrindFillLevel>0.000</RegrindFillLevel>
              <RegrindPercentage>0.000</RegrindPercentage>
              <SetCapacity>0.000</SetCapacity>
              <SetDosingPercentage>100.000</SetDosingPercentage>
              <Tolerance>25.000</Tolerance>
              <ValveType>None</ValveType>
            </Component>
          </Unit>
          <Unit Index="1" Name="Unit2" Type="MCBalance">
            <ActMotorSpeed>0.000</ActMotorSpeed>
            <BatchCounter>0</BatchCounter>
            <BatchWeight>0.000</BatchWeight>
            <DosingMode>Gravimetric</DosingMode>
            <DosingTool>GLX</DosingTool>
            <EventCodes />
            <GrossWeight>0.000</GrossWeight>
            <MaxEventType>None</MaxEventType>
            <SetMotorSpeed>0.000</SetMotorSpeed>
            <Status>Off</Status>
            <Component Index="0" Name="Component2" Type="AdditiveV">
              <ActCapacity>0.000</ActCapacity>
              <ActDosingPercentage>0.000</ActDosingPercentage>
              <BandNumber>0</BandNumber>
              <Consumption>0.000</Consumption>
              <Material>glx-ng</Material>
              <CorrectionFactor>1.000</CorrectionFactor>
              <EventCodes />
              <GranulateType>Normal</GranulateType>
              <MeasurementTime>0.000</MeasurementTime>
              <ActDosingWeight>0.000</ActDosingWeight>
              <SetDosingWeight>0.000</SetDosingWeight>
              <MaxEventType>None</MaxEventType>
              <PumpType>None</PumpType>
              <PumpSize>None</PumpSize>
              <RegrindFillLevel>0.000</RegrindFillLevel>
              <RegrindPercentage>0.000</RegrindPercentage>
              <SetCapacity>0.000</SetCapacity>
              <SetDosingPercentage>10.000</SetDosingPercentage>
              <Tolerance>25.000</Tolerance>
              <ValveType>None</ValveType>
            </Component>
          </Unit>
        </Group>
      </Machine>
    </m:AddProcessLog>
  </Body>
</Envelope>

AddProcessLog:

public class ProcessLog
{
    [XmlAttribute] 
    public string Version { get; set; }

    [XmlElement(Namespace = "", ElementName = "Machine")] 
    public MachineLog MachineLog { get; set; }
}

UpdateStructure:

public class UpdateStructure
{
    [XmlAttribute] public string Version { get; set; }
    
    [XmlElement(Namespace = "", ElementName = "Machine" )] 
    public Machine Machine { get; set; }
}

Now I get the following exception:

 ---> System.InvalidOperationException: There was an error reflecting property 'Body'.
 ---> System.InvalidOperationException: There was an error reflecting type 'MCSmart2.Shared.Models.CommunicationModels.Body'.
 ---> System.InvalidOperationException: There was an error reflecting property 'UpdateStructure'.
 ---> System.InvalidOperationException: There was an error reflecting type 'MCSmart2.Shared.Models.CommunicationModels.UpdateStructure'.
 ---> System.InvalidOperationException: There was an error reflecting property 'Machine'.
 ---> System.InvalidOperationException: The top XML element 'Machine' from namespace '' references distinct types MCSmart2.Shared.Models.CommunicationModels.MachineLog and MCSmart2.Shared.Models.CommunicationModels.Machine. Use XML attributes to specify another XML name or namespace for the element or types.
   at System.Xml.Serialization.XmlReflectionImporter.ReconcileAccessor(Accessor accessor, NameTable accessors)
   at System.Xml.Serialization.XmlReflectionImporter.ReconcileLocalAccessor(ElementAccessor accessor, String ns)
   at System.Xml.Serialization.XmlReflectionImporter.ImportAccessorMapping(MemberMapping accessor, FieldModel model, XmlAttributes a, String ns, Type choiceIdentifierType, Boolean rpc, Boolean openModel, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportFieldMapping(StructModel parent, FieldModel model, XmlAttributes a, String ns, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.InitializeStructMembers(StructMapping mapping, StructModel model, Boolean openModel, String typeName, RecursionLimiter limiter)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlReflectionImporter.InitializeStructMembers(StructMapping mapping, StructModel model, Boolean openModel, String typeName, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportStructLikeMapping(StructModel model, String ns, Boolean openModel, XmlAttributes a, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportAccessorMapping(MemberMapping accessor, FieldModel model, XmlAttributes a, String ns, Type choiceIdentifierType, Boolean rpc, Boolean openModel, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportFieldMapping(StructModel parent, FieldModel model, XmlAttributes a, String ns, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.InitializeStructMembers(StructMapping mapping, StructModel model, Boolean openModel, String typeName, RecursionLimiter limiter)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlReflectionImporter.InitializeStructMembers(StructMapping mapping, StructModel model, Boolean openModel, String typeName, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportStructLikeMapping(StructModel model, String ns, Boolean openModel, XmlAttributes a, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportAccessorMapping(MemberMapping accessor, FieldModel model, XmlAttributes a, String ns, Type choiceIdentifierType, Boolean rpc, Boolean openModel, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportFieldMapping(StructModel parent, FieldModel model, XmlAttributes a, String ns, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.InitializeStructMembers(StructMapping mapping, StructModel model, Boolean openModel, String typeName, RecursionLimiter limiter)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlReflectionImporter.InitializeStructMembers(StructMapping mapping, StructModel model, Boolean openModel, String typeName, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportStructLikeMapping(StructModel model, String ns, Boolean openModel, XmlAttributes a, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportElement(TypeModel model, XmlRootAttribute root, String defaultNamespace, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type, XmlRootAttribute root, String defaultNamespace)
   at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace)
   at MCSmart.CommunicationModule.Parsers.XmlObjectParser`1.XmlToObject(Byte[] data) in C:\Users\NickdeBoerMovacolor\Movacolor\MCSmart\backend\MCSmart.CommunicationModule\Parsers\XmlObjectParser.cs:line 15
   at MCSmart.CommunicationModule.Communication.Connections.MctcConnection.MctcConnection.ReadDataAsync() in C:\Users\NickdeBoerMovacolor\Movacolor\MCSmart\backend\MCSmart.CommunicationModule\Communication\Connections\MctcConnection\MctcConnection.cs:line 72

Does anyone know how I can fix this?

dbc
  • 104,963
  • 20
  • 228
  • 340
  • If you look at the error message, it suggests that a namespace qualifier is needed in the class attributes. Note that the properties you are deserializing are scoped by a namespace in the XML document. Also note, that you've specifically specified that there is no namespace in the attributes of the class. I'm not sure if there's inference at play normally but that's likely to affect it. – Aluan Haddad May 09 '23 at 08:05
  • 1
    Where's your actual code? – Enigmativity May 09 '23 at 08:06
  • Can you modify the xml? Serialization allows an inherited class, but requires a 'type' namespace to be added in the xml file. The other way is to use a custom xml serializer : https://learn.microsoft.com/en-us/dotnet/api/system.xml.serialization.ixmlserializable?view=net-7.0&force_isolation=true – jdweng May 09 '23 at 09:25
  • Please [edit] your question to share a [mcve] that reproduces the exception shown. See [ask] for why this will increase your chances of getting useful help. – dbc May 11 '23 at 02:23

1 Answers1

-1

You don't show the full schema for your <Body> element, but from your c# classes it seems have a choice schema with one of two possible child elements:

<Body>
    <m:AddProcessLog xmlns:m="urn:mcsmart:machine" Version="2.1.0">
        <!--Contents shown in question-->
    </m:AddProcessLog>
<Body>

Or

<Body>
    <m:UpdateStructure xmlns:m="urn:mcsmart:machine" Version="2.1.0">
        <!--Contents not shown in question-->
    </m:UpdateStructure>
<Body>

Assuming that is correct, you can bind to such a schema by introducing a polymorphic type hierarchy for AddProcessLog and UpdateStructure like so:

public abstract class BodyValueBase
{
    [XmlAttribute] public string Version { get; set; }
}

[XmlRoot(ElementName="AddProcessLog", Namespace="")]
public class AddProcessLog : BodyValueBase
{
    [XmlElement(ElementName="Machine")]
    public MachineLog MachineLog { get; set; } // The type MachineLog is not shown in the question
}

[XmlRoot(ElementName="UpdateStructure", Namespace="")]
public class UpdateStructure : BodyValueBase
{
    [XmlElement(Namespace = "", ElementName = "Machine" )] 
    public Machine Machine { get; set; } // The type MachineLog is not shown in the question
}

And then your root classes corresponding to <Envelope>, <Body> and <Header> should look like:

[XmlRoot(ElementName="Body")]
public class Body 
{
    [XmlElement("AddProcessLog", typeof(AddProcessLog), Namespace="urn:mcsmart:machine")]
    [XmlElement("UpdateStructure", typeof(UpdateStructure), Namespace="urn:mcsmart:machine")]
    public BodyValueBase BodyValue { get; set; }
}

[XmlRoot(ElementName="Header")]
public class Header 
{
    [XmlElement(ElementName="Sender")]
    public string Sender { get; set; }
    [XmlElement(ElementName="MacAddress")]
    public string MacAddress { get; set; }
}

[XmlRoot(ElementName="Envelope")] // The type Envelope is not shown in the question
public class Envelope 
{
    [XmlElement(ElementName="Header")] // The type Header is not shown in the question
    public Header Header { get; set; }

    [XmlElement(ElementName="Body")]
    public Body Body { get; set; }
}

Notes:

  1. There should be multiple [XmlElement(ElementName, Type, Namespace = namespace)] attributes applied to Body.BodyValue, one for each polymorphic subtype of BodyValueBase, with its ElementName corresponding to the appropriate derived Type.

  2. For documentation on binding polymorphic types to choice elements, see Choice Element Binding Support: Differentiating by Type.

    See also this answer by Marc Gravell to Using XmlSerializer to serialize derived classes for more on XmlSerializer support for polymorphism.

  3. I moved Version to the base class since it seems to be shared by both. You could remove it and add back into both derived types if you prefer.

Demo fiddle here.

dbc
  • 104,963
  • 20
  • 228
  • 340