IXmlSerializable ReadXml implementation












1















I am trying to get the name of an XML tag into a class property when performing XML deserialization. I need the name as a property since multiple XML tags share the same class. The XML and associated classes are defined below.



I have an XML response which I receive in the format:



<Data totalExecutionTime="00:00:00.0467241">
<ItemNumber id="1234" order="0" createdDate="2017-03-24T12:07:09.07" modifiedDate="2018-08-29T16:59:19.127">
<Value modifiedDate="2017-03-24T12:07:12.77">ABC1234</Value>
<Category id="5432" parentID="9876" itemOrder="0" modifiedDate="2017-03-24T12:16:23.687">The best category</Category>
... <!-- like 100 other elements -->
</ItemNumber>
</Data>


Deserialize done as follows:



XmlSerializer serializer = new XmlSerializer(typeof(ItemData));
using (TextReader reader = new StringReader(response))
{
ItemData itemData = (ItemData)serializer.Deserialize(reader);
}


And a class for the top level, ItemData:



[Serializable]
[XmlRoot("Data")]
public class ItemData
{
[XmlAttribute("totalExecutionTime")]
public string ExecutionTime { get; set; }

[XmlElement("ItemNumber", Type = typeof(ItemBase))]
public List<ItemBase> Items { get; set; }
}


ItemBase is defined as:



[Serializable]
public class ItemBase
{
[XmlElement("Value")]
public virtual ItemProperty ItemNumber { get; set; } = ItemProperty.Empty;

[XmlElement("ItemName")]
public virtual ItemProperty Category { get; set; } = ItemProperty.Empty;

... // like 100 other properties
}


And finally ItemProperty:



public class ItemProperty : IXmlSerializable
{
public static ItemProperty Empty { get; } = new ItemProperty();

public ItemProperty()
{
this.Name = string.Empty;
this.Value = string.Empty;
this.Id = 0;
this.Order = 0;
}

public string Name { get; set; }

[XmlText] // no effect while using IXmlSerializable
public string Value { get; set; }

[XmlAttribute("id")] // no effect while using IXmlSerializable
public int Id { get; set; }

[XmlAttribute("itemOrder")] // no effect while using IXmlSerializable
public int Order { get; set; }

public XmlSchema GetSchema()
{
return null;
}

public void ReadXml(XmlReader reader)
{
reader.MoveToContent();

string name = reader.Name;
this.Name = name;

string val = reader.ReadElementString();
this.Value = val;

if (reader.HasAttributes)
{
string id = reader.GetAttribute("id");
this.Id = Convert.ToInt32(id);

string itemOrder = reader.GetAttribute("itemOrder");
this.Order = Convert.ToInt32(itemOrder);

string sequence = reader.GetAttribute("seq");
this.Sequence = Convert.ToInt32(sequence);
}

// it seems the reader doesn't advance to the next element after reading
if (reader.NodeType == XmlNodeType.EndElement && !reader.IsEmptyElement)
{
reader.Read();
}
}

public void WriteXml(XmlWriter writer)
{
throw new NotImplementedException();
}
}


The point of implementing the IXmlSerializable interface is because ultimately I need the name of the XML tag that is stored as an ItemProperty and that information is not captured when using the XML class/property attributes. I believe this is the case since the attributes determine which class to use for the deserialization and normally each XML tag would have an associated class. I don't want to go that direction since there are such a large number of different tags that may be in the response and they all share similar attributes. Hence the ItemProperty class.



I've tried also passing the name of the tag via a parameterized constructor in the ItemProperty and setting the name property there, but when deserialization is performed, it uses the default constructor and then sets the property values, so that is not an option.



Reflection doesn't work either since the class is always ItemProperty and therefore doesn't have a unique name.



Maybe how I'm structuring the XML attributes could be done differently to achieve what I'm trying to do, but I don't see it.



I'm open to any way to solve this problem, but I'm pretty sure it entails implementing IXmlSerializable.ReadXml(). I know it is the job of the XmlReader to read the entirety of the XML and advance the reader to the end of the text, but I'm a little unclear on how to do that.



TLDR: How do I properly implement IXmlSerializable.ReadXml() while capturing the XML tag name, tag value, and all attributes into the class properties?



Edit: with the updated ReadXml method, I get all the data needed at the ItemProperty level, but class ItemData, the Items list only ever has one item. I assume because I am not advancing the reader properly.










share|improve this question

























  • Possibly related: Xmlserializer to C# object, store original XML element. Does that answer your question? It shows an example of loading the entire element corresponding to the IXmlSerializable node into an XElement.

    – dbc
    Dec 1 '18 at 0:48











  • Not exactly, since that example the element name being deserialized is known before runtime. I do get all of the data I need at the ItemProperty level, but I believe the reader isn't advancing since ItemData.Items only ever contains one item. See the edit at the bottom. Thanks.

    – jbook
    Dec 4 '18 at 21:18
















1















I am trying to get the name of an XML tag into a class property when performing XML deserialization. I need the name as a property since multiple XML tags share the same class. The XML and associated classes are defined below.



I have an XML response which I receive in the format:



<Data totalExecutionTime="00:00:00.0467241">
<ItemNumber id="1234" order="0" createdDate="2017-03-24T12:07:09.07" modifiedDate="2018-08-29T16:59:19.127">
<Value modifiedDate="2017-03-24T12:07:12.77">ABC1234</Value>
<Category id="5432" parentID="9876" itemOrder="0" modifiedDate="2017-03-24T12:16:23.687">The best category</Category>
... <!-- like 100 other elements -->
</ItemNumber>
</Data>


Deserialize done as follows:



XmlSerializer serializer = new XmlSerializer(typeof(ItemData));
using (TextReader reader = new StringReader(response))
{
ItemData itemData = (ItemData)serializer.Deserialize(reader);
}


And a class for the top level, ItemData:



[Serializable]
[XmlRoot("Data")]
public class ItemData
{
[XmlAttribute("totalExecutionTime")]
public string ExecutionTime { get; set; }

[XmlElement("ItemNumber", Type = typeof(ItemBase))]
public List<ItemBase> Items { get; set; }
}


ItemBase is defined as:



[Serializable]
public class ItemBase
{
[XmlElement("Value")]
public virtual ItemProperty ItemNumber { get; set; } = ItemProperty.Empty;

[XmlElement("ItemName")]
public virtual ItemProperty Category { get; set; } = ItemProperty.Empty;

... // like 100 other properties
}


And finally ItemProperty:



public class ItemProperty : IXmlSerializable
{
public static ItemProperty Empty { get; } = new ItemProperty();

public ItemProperty()
{
this.Name = string.Empty;
this.Value = string.Empty;
this.Id = 0;
this.Order = 0;
}

public string Name { get; set; }

[XmlText] // no effect while using IXmlSerializable
public string Value { get; set; }

[XmlAttribute("id")] // no effect while using IXmlSerializable
public int Id { get; set; }

[XmlAttribute("itemOrder")] // no effect while using IXmlSerializable
public int Order { get; set; }

public XmlSchema GetSchema()
{
return null;
}

public void ReadXml(XmlReader reader)
{
reader.MoveToContent();

string name = reader.Name;
this.Name = name;

string val = reader.ReadElementString();
this.Value = val;

if (reader.HasAttributes)
{
string id = reader.GetAttribute("id");
this.Id = Convert.ToInt32(id);

string itemOrder = reader.GetAttribute("itemOrder");
this.Order = Convert.ToInt32(itemOrder);

string sequence = reader.GetAttribute("seq");
this.Sequence = Convert.ToInt32(sequence);
}

// it seems the reader doesn't advance to the next element after reading
if (reader.NodeType == XmlNodeType.EndElement && !reader.IsEmptyElement)
{
reader.Read();
}
}

public void WriteXml(XmlWriter writer)
{
throw new NotImplementedException();
}
}


The point of implementing the IXmlSerializable interface is because ultimately I need the name of the XML tag that is stored as an ItemProperty and that information is not captured when using the XML class/property attributes. I believe this is the case since the attributes determine which class to use for the deserialization and normally each XML tag would have an associated class. I don't want to go that direction since there are such a large number of different tags that may be in the response and they all share similar attributes. Hence the ItemProperty class.



I've tried also passing the name of the tag via a parameterized constructor in the ItemProperty and setting the name property there, but when deserialization is performed, it uses the default constructor and then sets the property values, so that is not an option.



Reflection doesn't work either since the class is always ItemProperty and therefore doesn't have a unique name.



Maybe how I'm structuring the XML attributes could be done differently to achieve what I'm trying to do, but I don't see it.



I'm open to any way to solve this problem, but I'm pretty sure it entails implementing IXmlSerializable.ReadXml(). I know it is the job of the XmlReader to read the entirety of the XML and advance the reader to the end of the text, but I'm a little unclear on how to do that.



TLDR: How do I properly implement IXmlSerializable.ReadXml() while capturing the XML tag name, tag value, and all attributes into the class properties?



Edit: with the updated ReadXml method, I get all the data needed at the ItemProperty level, but class ItemData, the Items list only ever has one item. I assume because I am not advancing the reader properly.










share|improve this question

























  • Possibly related: Xmlserializer to C# object, store original XML element. Does that answer your question? It shows an example of loading the entire element corresponding to the IXmlSerializable node into an XElement.

    – dbc
    Dec 1 '18 at 0:48











  • Not exactly, since that example the element name being deserialized is known before runtime. I do get all of the data I need at the ItemProperty level, but I believe the reader isn't advancing since ItemData.Items only ever contains one item. See the edit at the bottom. Thanks.

    – jbook
    Dec 4 '18 at 21:18














1












1








1








I am trying to get the name of an XML tag into a class property when performing XML deserialization. I need the name as a property since multiple XML tags share the same class. The XML and associated classes are defined below.



I have an XML response which I receive in the format:



<Data totalExecutionTime="00:00:00.0467241">
<ItemNumber id="1234" order="0" createdDate="2017-03-24T12:07:09.07" modifiedDate="2018-08-29T16:59:19.127">
<Value modifiedDate="2017-03-24T12:07:12.77">ABC1234</Value>
<Category id="5432" parentID="9876" itemOrder="0" modifiedDate="2017-03-24T12:16:23.687">The best category</Category>
... <!-- like 100 other elements -->
</ItemNumber>
</Data>


Deserialize done as follows:



XmlSerializer serializer = new XmlSerializer(typeof(ItemData));
using (TextReader reader = new StringReader(response))
{
ItemData itemData = (ItemData)serializer.Deserialize(reader);
}


And a class for the top level, ItemData:



[Serializable]
[XmlRoot("Data")]
public class ItemData
{
[XmlAttribute("totalExecutionTime")]
public string ExecutionTime { get; set; }

[XmlElement("ItemNumber", Type = typeof(ItemBase))]
public List<ItemBase> Items { get; set; }
}


ItemBase is defined as:



[Serializable]
public class ItemBase
{
[XmlElement("Value")]
public virtual ItemProperty ItemNumber { get; set; } = ItemProperty.Empty;

[XmlElement("ItemName")]
public virtual ItemProperty Category { get; set; } = ItemProperty.Empty;

... // like 100 other properties
}


And finally ItemProperty:



public class ItemProperty : IXmlSerializable
{
public static ItemProperty Empty { get; } = new ItemProperty();

public ItemProperty()
{
this.Name = string.Empty;
this.Value = string.Empty;
this.Id = 0;
this.Order = 0;
}

public string Name { get; set; }

[XmlText] // no effect while using IXmlSerializable
public string Value { get; set; }

[XmlAttribute("id")] // no effect while using IXmlSerializable
public int Id { get; set; }

[XmlAttribute("itemOrder")] // no effect while using IXmlSerializable
public int Order { get; set; }

public XmlSchema GetSchema()
{
return null;
}

public void ReadXml(XmlReader reader)
{
reader.MoveToContent();

string name = reader.Name;
this.Name = name;

string val = reader.ReadElementString();
this.Value = val;

if (reader.HasAttributes)
{
string id = reader.GetAttribute("id");
this.Id = Convert.ToInt32(id);

string itemOrder = reader.GetAttribute("itemOrder");
this.Order = Convert.ToInt32(itemOrder);

string sequence = reader.GetAttribute("seq");
this.Sequence = Convert.ToInt32(sequence);
}

// it seems the reader doesn't advance to the next element after reading
if (reader.NodeType == XmlNodeType.EndElement && !reader.IsEmptyElement)
{
reader.Read();
}
}

public void WriteXml(XmlWriter writer)
{
throw new NotImplementedException();
}
}


The point of implementing the IXmlSerializable interface is because ultimately I need the name of the XML tag that is stored as an ItemProperty and that information is not captured when using the XML class/property attributes. I believe this is the case since the attributes determine which class to use for the deserialization and normally each XML tag would have an associated class. I don't want to go that direction since there are such a large number of different tags that may be in the response and they all share similar attributes. Hence the ItemProperty class.



I've tried also passing the name of the tag via a parameterized constructor in the ItemProperty and setting the name property there, but when deserialization is performed, it uses the default constructor and then sets the property values, so that is not an option.



Reflection doesn't work either since the class is always ItemProperty and therefore doesn't have a unique name.



Maybe how I'm structuring the XML attributes could be done differently to achieve what I'm trying to do, but I don't see it.



I'm open to any way to solve this problem, but I'm pretty sure it entails implementing IXmlSerializable.ReadXml(). I know it is the job of the XmlReader to read the entirety of the XML and advance the reader to the end of the text, but I'm a little unclear on how to do that.



TLDR: How do I properly implement IXmlSerializable.ReadXml() while capturing the XML tag name, tag value, and all attributes into the class properties?



Edit: with the updated ReadXml method, I get all the data needed at the ItemProperty level, but class ItemData, the Items list only ever has one item. I assume because I am not advancing the reader properly.










share|improve this question
















I am trying to get the name of an XML tag into a class property when performing XML deserialization. I need the name as a property since multiple XML tags share the same class. The XML and associated classes are defined below.



I have an XML response which I receive in the format:



<Data totalExecutionTime="00:00:00.0467241">
<ItemNumber id="1234" order="0" createdDate="2017-03-24T12:07:09.07" modifiedDate="2018-08-29T16:59:19.127">
<Value modifiedDate="2017-03-24T12:07:12.77">ABC1234</Value>
<Category id="5432" parentID="9876" itemOrder="0" modifiedDate="2017-03-24T12:16:23.687">The best category</Category>
... <!-- like 100 other elements -->
</ItemNumber>
</Data>


Deserialize done as follows:



XmlSerializer serializer = new XmlSerializer(typeof(ItemData));
using (TextReader reader = new StringReader(response))
{
ItemData itemData = (ItemData)serializer.Deserialize(reader);
}


And a class for the top level, ItemData:



[Serializable]
[XmlRoot("Data")]
public class ItemData
{
[XmlAttribute("totalExecutionTime")]
public string ExecutionTime { get; set; }

[XmlElement("ItemNumber", Type = typeof(ItemBase))]
public List<ItemBase> Items { get; set; }
}


ItemBase is defined as:



[Serializable]
public class ItemBase
{
[XmlElement("Value")]
public virtual ItemProperty ItemNumber { get; set; } = ItemProperty.Empty;

[XmlElement("ItemName")]
public virtual ItemProperty Category { get; set; } = ItemProperty.Empty;

... // like 100 other properties
}


And finally ItemProperty:



public class ItemProperty : IXmlSerializable
{
public static ItemProperty Empty { get; } = new ItemProperty();

public ItemProperty()
{
this.Name = string.Empty;
this.Value = string.Empty;
this.Id = 0;
this.Order = 0;
}

public string Name { get; set; }

[XmlText] // no effect while using IXmlSerializable
public string Value { get; set; }

[XmlAttribute("id")] // no effect while using IXmlSerializable
public int Id { get; set; }

[XmlAttribute("itemOrder")] // no effect while using IXmlSerializable
public int Order { get; set; }

public XmlSchema GetSchema()
{
return null;
}

public void ReadXml(XmlReader reader)
{
reader.MoveToContent();

string name = reader.Name;
this.Name = name;

string val = reader.ReadElementString();
this.Value = val;

if (reader.HasAttributes)
{
string id = reader.GetAttribute("id");
this.Id = Convert.ToInt32(id);

string itemOrder = reader.GetAttribute("itemOrder");
this.Order = Convert.ToInt32(itemOrder);

string sequence = reader.GetAttribute("seq");
this.Sequence = Convert.ToInt32(sequence);
}

// it seems the reader doesn't advance to the next element after reading
if (reader.NodeType == XmlNodeType.EndElement && !reader.IsEmptyElement)
{
reader.Read();
}
}

public void WriteXml(XmlWriter writer)
{
throw new NotImplementedException();
}
}


The point of implementing the IXmlSerializable interface is because ultimately I need the name of the XML tag that is stored as an ItemProperty and that information is not captured when using the XML class/property attributes. I believe this is the case since the attributes determine which class to use for the deserialization and normally each XML tag would have an associated class. I don't want to go that direction since there are such a large number of different tags that may be in the response and they all share similar attributes. Hence the ItemProperty class.



I've tried also passing the name of the tag via a parameterized constructor in the ItemProperty and setting the name property there, but when deserialization is performed, it uses the default constructor and then sets the property values, so that is not an option.



Reflection doesn't work either since the class is always ItemProperty and therefore doesn't have a unique name.



Maybe how I'm structuring the XML attributes could be done differently to achieve what I'm trying to do, but I don't see it.



I'm open to any way to solve this problem, but I'm pretty sure it entails implementing IXmlSerializable.ReadXml(). I know it is the job of the XmlReader to read the entirety of the XML and advance the reader to the end of the text, but I'm a little unclear on how to do that.



TLDR: How do I properly implement IXmlSerializable.ReadXml() while capturing the XML tag name, tag value, and all attributes into the class properties?



Edit: with the updated ReadXml method, I get all the data needed at the ItemProperty level, but class ItemData, the Items list only ever has one item. I assume because I am not advancing the reader properly.







c# .net deserialization xml-deserialization ixmlserializable






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 5 '18 at 0:34









dbc

55.6k876130




55.6k876130










asked Nov 28 '18 at 19:48









jbookjbook

375422




375422













  • Possibly related: Xmlserializer to C# object, store original XML element. Does that answer your question? It shows an example of loading the entire element corresponding to the IXmlSerializable node into an XElement.

    – dbc
    Dec 1 '18 at 0:48











  • Not exactly, since that example the element name being deserialized is known before runtime. I do get all of the data I need at the ItemProperty level, but I believe the reader isn't advancing since ItemData.Items only ever contains one item. See the edit at the bottom. Thanks.

    – jbook
    Dec 4 '18 at 21:18



















  • Possibly related: Xmlserializer to C# object, store original XML element. Does that answer your question? It shows an example of loading the entire element corresponding to the IXmlSerializable node into an XElement.

    – dbc
    Dec 1 '18 at 0:48











  • Not exactly, since that example the element name being deserialized is known before runtime. I do get all of the data I need at the ItemProperty level, but I believe the reader isn't advancing since ItemData.Items only ever contains one item. See the edit at the bottom. Thanks.

    – jbook
    Dec 4 '18 at 21:18

















Possibly related: Xmlserializer to C# object, store original XML element. Does that answer your question? It shows an example of loading the entire element corresponding to the IXmlSerializable node into an XElement.

– dbc
Dec 1 '18 at 0:48





Possibly related: Xmlserializer to C# object, store original XML element. Does that answer your question? It shows an example of loading the entire element corresponding to the IXmlSerializable node into an XElement.

– dbc
Dec 1 '18 at 0:48













Not exactly, since that example the element name being deserialized is known before runtime. I do get all of the data I need at the ItemProperty level, but I believe the reader isn't advancing since ItemData.Items only ever contains one item. See the edit at the bottom. Thanks.

– jbook
Dec 4 '18 at 21:18





Not exactly, since that example the element name being deserialized is known before runtime. I do get all of the data I need at the ItemProperty level, but I believe the reader isn't advancing since ItemData.Items only ever contains one item. See the edit at the bottom. Thanks.

– jbook
Dec 4 '18 at 21:18












1 Answer
1






active

oldest

votes


















0














From the documentation for IXmlSerializable.ReadXml(XmlReader):




When this method is called, the reader is positioned on the start tag that wraps the information for your type. ... When this method returns, it must have read the entire element from beginning to end, including all of its contents. Unlike the WriteXml method, the framework does not handle the wrapper element automatically. Your implementation must do so. Failing to observe these positioning rules may cause code to generate unexpected runtime exceptions or corrupt data.




Your ReadXml() can be modified to meet these requirements as follows:



public void ReadXml(XmlReader reader)
{
reader.MoveToContent();

this.Name = reader.LocalName; // Do not include the prefix (if present) in the Name.

if (reader.HasAttributes)
{
var id = reader.GetAttribute("id");
if (id != null)
// Since id is missing from some elements you might want to make it nullable
this.Id = XmlConvert.ToInt32(id);

var order = reader.GetAttribute("itemOrder");
if (order != null)
// Since itemOrder is missing from some elements you might want to make it nullable
this.Order = XmlConvert.ToInt32(order);

string sequence = reader.GetAttribute("seq");
//There is no Sequence property?
//this.Sequence = Convert.ToInt32(sequence);
}

// Read element value.
// This method reads the start tag, the contents of the element, and moves the reader past the end element tag.
// thus there is no need for an additional Read()
this.Value = reader.ReadElementContentAsString();
}


Notes:





  1. You are calling ReadElementString() whose documentation states:




    We recommend that you use the ReadElementContentAsString() method to read a text element.




    As suggested, I modified your ReadXml() to use this method. In turn, its documentation states:




    This method reads the start tag, the contents of the element, and moves the reader past the end element tag.




    Thus this method should leave the XmlReader positioned exactly as required by ReadXml(), ensuring the reader is advanced properly.



  2. The XML attributes of each ItemProperty element must be processed before that element's content is read, since reading the content advances the reader past the element start -- and its attributes.


  3. Utilities from the XmlConvert class should be used to parse and format XML primitives so that numerical and date/time values are not erroneously localized.


  4. You probably don't want to include the namespace prefix (if any) in the Name property.



Demo fiddle here.






share|improve this answer
























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53527040%2fixmlserializable-readxml-implementation%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    0














    From the documentation for IXmlSerializable.ReadXml(XmlReader):




    When this method is called, the reader is positioned on the start tag that wraps the information for your type. ... When this method returns, it must have read the entire element from beginning to end, including all of its contents. Unlike the WriteXml method, the framework does not handle the wrapper element automatically. Your implementation must do so. Failing to observe these positioning rules may cause code to generate unexpected runtime exceptions or corrupt data.




    Your ReadXml() can be modified to meet these requirements as follows:



    public void ReadXml(XmlReader reader)
    {
    reader.MoveToContent();

    this.Name = reader.LocalName; // Do not include the prefix (if present) in the Name.

    if (reader.HasAttributes)
    {
    var id = reader.GetAttribute("id");
    if (id != null)
    // Since id is missing from some elements you might want to make it nullable
    this.Id = XmlConvert.ToInt32(id);

    var order = reader.GetAttribute("itemOrder");
    if (order != null)
    // Since itemOrder is missing from some elements you might want to make it nullable
    this.Order = XmlConvert.ToInt32(order);

    string sequence = reader.GetAttribute("seq");
    //There is no Sequence property?
    //this.Sequence = Convert.ToInt32(sequence);
    }

    // Read element value.
    // This method reads the start tag, the contents of the element, and moves the reader past the end element tag.
    // thus there is no need for an additional Read()
    this.Value = reader.ReadElementContentAsString();
    }


    Notes:





    1. You are calling ReadElementString() whose documentation states:




      We recommend that you use the ReadElementContentAsString() method to read a text element.




      As suggested, I modified your ReadXml() to use this method. In turn, its documentation states:




      This method reads the start tag, the contents of the element, and moves the reader past the end element tag.




      Thus this method should leave the XmlReader positioned exactly as required by ReadXml(), ensuring the reader is advanced properly.



    2. The XML attributes of each ItemProperty element must be processed before that element's content is read, since reading the content advances the reader past the element start -- and its attributes.


    3. Utilities from the XmlConvert class should be used to parse and format XML primitives so that numerical and date/time values are not erroneously localized.


    4. You probably don't want to include the namespace prefix (if any) in the Name property.



    Demo fiddle here.






    share|improve this answer




























      0














      From the documentation for IXmlSerializable.ReadXml(XmlReader):




      When this method is called, the reader is positioned on the start tag that wraps the information for your type. ... When this method returns, it must have read the entire element from beginning to end, including all of its contents. Unlike the WriteXml method, the framework does not handle the wrapper element automatically. Your implementation must do so. Failing to observe these positioning rules may cause code to generate unexpected runtime exceptions or corrupt data.




      Your ReadXml() can be modified to meet these requirements as follows:



      public void ReadXml(XmlReader reader)
      {
      reader.MoveToContent();

      this.Name = reader.LocalName; // Do not include the prefix (if present) in the Name.

      if (reader.HasAttributes)
      {
      var id = reader.GetAttribute("id");
      if (id != null)
      // Since id is missing from some elements you might want to make it nullable
      this.Id = XmlConvert.ToInt32(id);

      var order = reader.GetAttribute("itemOrder");
      if (order != null)
      // Since itemOrder is missing from some elements you might want to make it nullable
      this.Order = XmlConvert.ToInt32(order);

      string sequence = reader.GetAttribute("seq");
      //There is no Sequence property?
      //this.Sequence = Convert.ToInt32(sequence);
      }

      // Read element value.
      // This method reads the start tag, the contents of the element, and moves the reader past the end element tag.
      // thus there is no need for an additional Read()
      this.Value = reader.ReadElementContentAsString();
      }


      Notes:





      1. You are calling ReadElementString() whose documentation states:




        We recommend that you use the ReadElementContentAsString() method to read a text element.




        As suggested, I modified your ReadXml() to use this method. In turn, its documentation states:




        This method reads the start tag, the contents of the element, and moves the reader past the end element tag.




        Thus this method should leave the XmlReader positioned exactly as required by ReadXml(), ensuring the reader is advanced properly.



      2. The XML attributes of each ItemProperty element must be processed before that element's content is read, since reading the content advances the reader past the element start -- and its attributes.


      3. Utilities from the XmlConvert class should be used to parse and format XML primitives so that numerical and date/time values are not erroneously localized.


      4. You probably don't want to include the namespace prefix (if any) in the Name property.



      Demo fiddle here.






      share|improve this answer


























        0












        0








        0







        From the documentation for IXmlSerializable.ReadXml(XmlReader):




        When this method is called, the reader is positioned on the start tag that wraps the information for your type. ... When this method returns, it must have read the entire element from beginning to end, including all of its contents. Unlike the WriteXml method, the framework does not handle the wrapper element automatically. Your implementation must do so. Failing to observe these positioning rules may cause code to generate unexpected runtime exceptions or corrupt data.




        Your ReadXml() can be modified to meet these requirements as follows:



        public void ReadXml(XmlReader reader)
        {
        reader.MoveToContent();

        this.Name = reader.LocalName; // Do not include the prefix (if present) in the Name.

        if (reader.HasAttributes)
        {
        var id = reader.GetAttribute("id");
        if (id != null)
        // Since id is missing from some elements you might want to make it nullable
        this.Id = XmlConvert.ToInt32(id);

        var order = reader.GetAttribute("itemOrder");
        if (order != null)
        // Since itemOrder is missing from some elements you might want to make it nullable
        this.Order = XmlConvert.ToInt32(order);

        string sequence = reader.GetAttribute("seq");
        //There is no Sequence property?
        //this.Sequence = Convert.ToInt32(sequence);
        }

        // Read element value.
        // This method reads the start tag, the contents of the element, and moves the reader past the end element tag.
        // thus there is no need for an additional Read()
        this.Value = reader.ReadElementContentAsString();
        }


        Notes:





        1. You are calling ReadElementString() whose documentation states:




          We recommend that you use the ReadElementContentAsString() method to read a text element.




          As suggested, I modified your ReadXml() to use this method. In turn, its documentation states:




          This method reads the start tag, the contents of the element, and moves the reader past the end element tag.




          Thus this method should leave the XmlReader positioned exactly as required by ReadXml(), ensuring the reader is advanced properly.



        2. The XML attributes of each ItemProperty element must be processed before that element's content is read, since reading the content advances the reader past the element start -- and its attributes.


        3. Utilities from the XmlConvert class should be used to parse and format XML primitives so that numerical and date/time values are not erroneously localized.


        4. You probably don't want to include the namespace prefix (if any) in the Name property.



        Demo fiddle here.






        share|improve this answer













        From the documentation for IXmlSerializable.ReadXml(XmlReader):




        When this method is called, the reader is positioned on the start tag that wraps the information for your type. ... When this method returns, it must have read the entire element from beginning to end, including all of its contents. Unlike the WriteXml method, the framework does not handle the wrapper element automatically. Your implementation must do so. Failing to observe these positioning rules may cause code to generate unexpected runtime exceptions or corrupt data.




        Your ReadXml() can be modified to meet these requirements as follows:



        public void ReadXml(XmlReader reader)
        {
        reader.MoveToContent();

        this.Name = reader.LocalName; // Do not include the prefix (if present) in the Name.

        if (reader.HasAttributes)
        {
        var id = reader.GetAttribute("id");
        if (id != null)
        // Since id is missing from some elements you might want to make it nullable
        this.Id = XmlConvert.ToInt32(id);

        var order = reader.GetAttribute("itemOrder");
        if (order != null)
        // Since itemOrder is missing from some elements you might want to make it nullable
        this.Order = XmlConvert.ToInt32(order);

        string sequence = reader.GetAttribute("seq");
        //There is no Sequence property?
        //this.Sequence = Convert.ToInt32(sequence);
        }

        // Read element value.
        // This method reads the start tag, the contents of the element, and moves the reader past the end element tag.
        // thus there is no need for an additional Read()
        this.Value = reader.ReadElementContentAsString();
        }


        Notes:





        1. You are calling ReadElementString() whose documentation states:




          We recommend that you use the ReadElementContentAsString() method to read a text element.




          As suggested, I modified your ReadXml() to use this method. In turn, its documentation states:




          This method reads the start tag, the contents of the element, and moves the reader past the end element tag.




          Thus this method should leave the XmlReader positioned exactly as required by ReadXml(), ensuring the reader is advanced properly.



        2. The XML attributes of each ItemProperty element must be processed before that element's content is read, since reading the content advances the reader past the element start -- and its attributes.


        3. Utilities from the XmlConvert class should be used to parse and format XML primitives so that numerical and date/time values are not erroneously localized.


        4. You probably don't want to include the namespace prefix (if any) in the Name property.



        Demo fiddle here.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Dec 5 '18 at 0:30









        dbcdbc

        55.6k876130




        55.6k876130
































            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53527040%2fixmlserializable-readxml-implementation%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks

            Calculate evaluation metrics using cross_val_predict sklearn

            Insert data from modal to MySQL (multiple modal on website)