IXmlSerializable ReadXml implementation
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
add a comment |
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
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 theIXmlSerializable
node into anXElement
.
– 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 theItemProperty
level, but I believe the reader isn't advancing sinceItemData.Items
only ever contains one item. See the edit at the bottom. Thanks.
– jbook
Dec 4 '18 at 21:18
add a comment |
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
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
c# .net deserialization xml-deserialization ixmlserializable
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 theIXmlSerializable
node into anXElement
.
– 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 theItemProperty
level, but I believe the reader isn't advancing sinceItemData.Items
only ever contains one item. See the edit at the bottom. Thanks.
– jbook
Dec 4 '18 at 21:18
add a comment |
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 theIXmlSerializable
node into anXElement
.
– 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 theItemProperty
level, but I believe the reader isn't advancing sinceItemData.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
add a comment |
1 Answer
1
active
oldest
votes
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:
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 byReadXml()
, ensuring the reader is advanced properly.
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.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.You probably don't want to include the namespace prefix (if any) in the
Name
property.
Demo fiddle here.
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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:
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 byReadXml()
, ensuring the reader is advanced properly.
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.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.You probably don't want to include the namespace prefix (if any) in the
Name
property.
Demo fiddle here.
add a comment |
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:
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 byReadXml()
, ensuring the reader is advanced properly.
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.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.You probably don't want to include the namespace prefix (if any) in the
Name
property.
Demo fiddle here.
add a comment |
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:
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 byReadXml()
, ensuring the reader is advanced properly.
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.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.You probably don't want to include the namespace prefix (if any) in the
Name
property.
Demo fiddle here.
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:
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 byReadXml()
, ensuring the reader is advanced properly.
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.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.You probably don't want to include the namespace prefix (if any) in the
Name
property.
Demo fiddle here.
answered Dec 5 '18 at 0:30
dbcdbc
55.6k876130
55.6k876130
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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 anXElement
.– 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 sinceItemData.Items
only ever contains one item. See the edit at the bottom. Thanks.– jbook
Dec 4 '18 at 21:18