I have small problem - XML deserialization completely ignores items, which are out of alphabetic order. In example object (description in end of question), Birthday node is after FirstName node, and it is ignored and assigned default value after deserialization. Same for any other types and names (I had node CaseId of Guid type after node Patient of PatientInfo type, and after deserialization it had default value).
I'm serializing it in one application, using next code:
public static string SerializeToString(object data)
{
if (data == null) return null;
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
// what should the XmlWriter do?
var settings = new XmlWriterSettings
{
OmitXmlDeclaration = true,
NewLineChars = ""
};
using (var stringwriter = new System.IO.StringWriter())
{
// Use an XmlWriter to wrap the StringWriter
using (var xmlWriter = XmlWriter.Create(stringwriter, settings))
{
var serializer = new XmlSerializer(data.GetType(), "");
// serialize to the XmlWriter instance
serializer.Serialize(xmlWriter, data, ns);
return stringwriter.ToString();
}
}
}
Such approach was used to get proper result as argument for WebMethod (full problem described here). Results are something like this:
<PatientInfo><FirstName>Foo</FirstName><Birthday>2015-12-19T16:21:48.4009949+01:00</Birthday><RequestedClientID>00000000-0000-0000-0000-000000000000</RequestedClientID>00000000-0000-0000-0000-000000000000</patientId></PatientInfo>
Also I'm deserializing it in another application in simple manner
public static T Deserialize<T>(string xmlText)
{
if (String.IsNullOrEmpty(xmlText)) return default(T);
using (var stringReader = new StringReader(xmlText))
{
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
}
Example object:
[XmlRoot("PatientInfo")]
public class PatientInfo
{
[XmlElement("FirstName")]
public string FirstName { get; set; }
[XmlElement("LastName")]
public string LastName { get; set; }
[XmlElement("SSN")]
public string SSN { get; set; }
[XmlElement("Birthday")]
public DateTime? Birthday { get; set; }
[XmlElement("RequestedClientID")]
public Guid RequestedClientID { get; set; }
[XmlElement("patientId")]
public Guid patientId { get; set; }
}
So, I'd like to have answer for one of two questions - 1) How can I adjust my serialization to have all items in alphabetical order? 2) How can I adjust my deserialization, so it won't ignore items out of alphabetical order?
Any help is appreciated.
Update:
Just figured out, that deserialization method I'm using is not actually used at all in my problem, since I'm using serialized info as data with WebMethod, and it is deserialized with some internal mechanism of WCF.
WCF uses DataContractSerializer. This serializer is sensitive to XML element order, see Data Member Order. There's no quick way to disable this, instead you need to replace the serializer with XmlSerializer.
To do this, see Using the XmlSerializer Class, then and apply [XmlSerializerFormat] to your service, for instance:
[ServiceContract]
[XmlSerializerFormat]
public interface IPatientInfoService
{
[OperationContract]
public void ProcessPatientInfo(PatientInfo patient)
{
// Code not shown.
}
}
[XmlRoot("PatientInfo")]
public class PatientInfo
{
[XmlElement("FirstName")]
public string FirstName { get; set; }
[XmlElement("LastName")]
public string LastName { get; set; }
[XmlElement("SSN")]
public string SSN { get; set; }
[XmlElement("Birthday")]
public DateTime? Birthday { get; set; }
[XmlElement("RequestedClientID")]
public Guid RequestedClientID { get; set; }
[XmlElement("patientId")]
public Guid patientId { get; set; }
}
Related
I have a custom list used to have features of BindingList<T> while also being XML serializable. The SBindingList<T> class is as follows:
public class SBindingList<T> : BindingList<T>, IXmlSerializable
{
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
reader.Read();
XmlSerializer serializer = new XmlSerializer(typeof(List<T>));
List<T> ts = (List<T>)serializer.Deserialize(reader);
foreach (T item in ts)
{
Add(item);
}
}
public void WriteXml(XmlWriter writer)
{
XmlSerializer serializer = new XmlSerializer(typeof(List<T>));
serializer.Serialize(writer, this.ToList());
}
}
The idea is to read and write the XML as if it was a List<T> behind the scenes but still works and acts like a BindingList<T>. The issue I'm having is that it starts reading <Games> then when it gets to Add(item); starts to deserialize <AvailableQuests>. Because <AvailableQuests> is empty it just falls out of ReadXml but goes immediately back finishing up adding <Games> and just falls out of ReadXml without even touching <Characters> or <ActiveQuests>. The <Games>, <AvailableQuests>, <Characters>, and <ActiveQuests> are all SBindingList<T>s. Also note that I redacted all the IDs for privacy reasons. Just replace the [blah] tags with any ulong.
<Games>
<ArrayOfGame xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Game GuildID="[GuildID]" BotChannel="0" QuestChannel="0">
<AvailableQuests>
<ArrayOfQuest />
</AvailableQuests>
<Characters>
<ArrayOfCharacter>
<Character Name="The Real Dirty Dan" Class="Meme" Level="17"
OwnerID="[Owner1]" Busy="false" />
<Character Name="Bob" Class="Builder" Level="2" OwnerID="[Owner2]"
Busy="false" />
</ArrayOfCharacter>
</Characters>
<ActiveQuests>
<ArrayOfQuest />
</ActiveQuests>
</Game>
</ArrayOfGame>
</Games>
Here is the object setup incase someone needs it:
public class Game
{
/// <summary>
/// Only for serialization
/// </summary>
public Game() { }
public Game(ulong guildID)
{
GuildID = guildID;
}
/// <summary>
/// What discord server is the game on
/// </summary>
[XmlAttribute]
public ulong GuildID { get; set; }
[XmlAttribute]
public ulong BotChannel { get; set; }
[XmlAttribute]
public ulong QuestChannel { get; set; }
public SBindingList<Quest> AvailableQuests { get; set; } = new SBindingList<Quest>();
public SBindingList<Character> Characters { get; set; } = new SBindingList<Character>();
public SBindingList<Quest> ActiveQuests { get; set; } = new SBindingList<Quest>();
public string Test { get; set; } // This gets ignored too
public Character GetCharacterByName(string name)
{
return (from c in Characters
where c.Name == name
select c).FirstOrDefault();
}
}
I don't really know where to start with this one. I've tried using a List<T> or just reading each T one at a time but both ways end up ignoring all other elements. My only guess is that I need to clean something up with the reader before I can let it fall out of ReadXml like reader.FinishedReading().
The answer to this lies in the documentation for ReadXml:
When this method is called, the reader is positioned on the start tag that wraps the information for your type. That is, directly on the start tag that indicates the beginning of a serialized object. 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.
You seem to have spotted this by calling reader.Read() at the start (to move past the start tag and onto the content), but you haven't at the end. The fix is to call reader.Read() again afterwards.
Perhaps a better approach would be to call reader.ReadStartElement() first and reader.ReadEndElement() at the end. This just calls reader.Read() internally, but it will throw an exception in the case the reader isn't in the expected state.
I'd also suggest sharing a static serialiser instance rather than creating a new one each time you read or write a list. So this should work:
public class SBindingList<T> : BindingList<T>, IXmlSerializable
{
private static readonly XmlSerializer Serializer = new XmlSerializer(typeof(List<T>));
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement();
var items = (List<T>)Serializer.Deserialize(reader);
foreach (T item in items)
{
Add(item);
}
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
Serializer.Serialize(writer, this.ToList());
}
}
If reader is on any other element besides the next element, the reader gives up deserizing the rest of the object. In other words, the reader must be on the next element (In this case the Character element) to continue to deserialize the <Game> tag. In this case it can be fixed by using reader.Skip() then reader.ReadEndElement()
The reader needs to be at the next element (in this case <Characters>) in the original object otherwise it just returns if it is at a end element node. This solution isn't failproof or the cleanest but it gets the job done. If I find something better that confirms that it is at the next elemet before falling out I will edit this answer.
public void ReadXml(XmlReader reader)
{
reader.Read();
XmlSerializer serializer = new XmlSerializer(typeof(List<T>));
XmlReader xmlReader = reader.ReadSubtree();
xmlReader.Read();
List<T> ts = (List<T>)serializer.Deserialize(xmlReader);
foreach (T item in ts)
{
Add(item);
}
xmlReader.Close();
reader.Skip();
reader.ReadEndElement();
}
The key is reader.Skip() and reader.ReadEndElement() lines. The Skip() function moves the reader to </AvailableQuests> and the ReadEndElement() makes sure that it is an end element (Like it needs to be) and moves to <Characters>.
Edit: use https://stackoverflow.com/a/71969803/4771954
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication23
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Games));
Games games = (Games)serializer.Deserialize(reader);
}
}
public class Games
{
[XmlArray(ElementName = "ArrayOfGame")]
[XmlArrayItem(ElementName = "Game")]
public List<Game> game { get; set; }
}
public class Game
{
[XmlAttribute()]
public string GuildID { get; set; }
[XmlAttribute()]
public int BotChannel { get; set; }
[XmlAttribute()]
public int QuestChannel { get; set; }
[XmlArray(ElementName = "AvailableQuests")]
[XmlArrayItem(ElementName = "ArrayOfQuest")]
public List<Quest> availableQuest { get; set; }
[XmlElement(ElementName = "Characters")]
public Characters characters { get; set; }
[XmlArray(ElementName = "ActiveQuests")]
[XmlArrayItem(ElementName = "ArrayOfQuest")]
public List<Quest> activeQuest { get; set; }
public class Quest
{
}
public class Characters
{
[XmlArray(ElementName = "ArrayOfCharacter")]
[XmlArrayItem(ElementName = "Character")]
public List<Character> character { get; set; }
}
public class Character
{
[XmlAttribute()]
public string Name { get; set; }
[XmlAttribute()]
public string Class { get; set; }
[XmlAttribute()]
public int Level { get; set; }
[XmlAttribute()]
public string OwnerID { get; set; }
[XmlAttribute()]
public Boolean Busy{ get; set; }
}
public class ArrayOfQuest
{
}
}}
I want to serialize an object with DataMember attribute to be ignored on some properties.
Say I have custom attribute MyIgnoreDataMember.
I want properties marked with it to be invisible for my custom DataContractSerializer, but visible for the normal DataContractSerializer.
And I have to use DataContractSerializer and nothing else.
The code is a Silverlight app.
Anyone have done subclassing DataContractSerializer successfully?
An answer to your question is complicated by the following issues:
DataContractSerializer is sealed, so can't be subclassed to check for an attribute like MyIgnoreDataMember.
Using a serialization surrogate to inject an appropriate DTO into your object graph during serialization would normally be the way to go -- but it looks like it's not available on silverlight, see this answer.
DataContractSerializer does not support the ShouldSerialize pattern, as explained here, so you can't just suppress serialization of the undesired properties via a callback method.
So, what are your options? Let's say your object graph looks like the following:
[DataContract(Name = "Root", Namespace = "http://www.MyNamespace.com")]
public class RootObject
{
[DataMember]
public NestedObject NestedObject { get; set; }
}
[DataContract(Name = "Nested", Namespace = "http://www.MyNamespace.com")]
public class NestedObject
{
[DataMember]
public string SensitiveData { get; set; }
[DataMember]
public string PublicData { get; set; }
}
And you want to conditionally suppress output of SensitiveData. Then the following are possibilities:
If you only need to eliminate a few properties, you could mark them with EmitDefaultValue = false and return a default value when some thread static is true, for instance:
[DataContract(Name = "Root", Namespace = "http://www.MyNamespace.com")]
public class RootObject
{
[DataMember]
public NestedObject NestedObject { get; set; }
}
[DataContract(Name = "Nested", Namespace = "http://www.MyNamespace.com")]
public class NestedObject
{
string sensitiveData;
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public string SensitiveData
{
get
{
if (SerializationState.InCustomSerialization())
return null;
return sensitiveData;
}
set
{
sensitiveData = value;
}
}
[DataMember]
public string PublicData { get; set; }
}
public static class SerializationState
{
[ThreadStatic]
static bool inCustomSerialization;
public static bool InCustomSerialization()
{
return inCustomSerialization;
}
public static IDisposable SetInCustomDeserialization(bool value)
{
return new PushValue<bool>(value, () => inCustomSerialization, b => inCustomSerialization = b);
}
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
#region IDisposable Members
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
#endregion
}
And then, when serializing, do something like:
using (SerializationState.SetInCustomDeserialization(true))
{
// Serialize with data contract serializer.
}
Honestly quite ugly.
You could make an entire DTO hierarchy with the same contract names and namespaces as your real types, map the real classes to the DTO with something like automapper, and serialize the DTOs:
[DataContract(Name = "Root", Namespace = "http://www.MyNamespace.com")]
class RootObjectDTO
{
[DataMember]
public NestedObjectDTO NestedObject { get; set; }
}
[DataContract(Name = "Nested", Namespace = "http://www.MyNamespace.com")]
class NestedObjectDTO
{
[DataMember]
public string PublicData { get; set; }
}
If automapper is not available on silverlight, you can use DataContractSerializer itself to do the mapping, since the contract names and namespaces are identical. I.e. - serialize the real root object to an XML string (or to an XDocument as shown below), deserialize the intermediate XML to the DTO root, then serialize out the DTO.
You could serialize to an in-memory XDocument (which is available in silverlight) using the following extension class:
public static partial class DataContractSerializerHelper
{
public static XDocument SerializeContractToXDocument<T>(this T obj)
{
return obj.SerializeContractToXDocument(null);
}
public static XDocument SerializeContractToXDocument<T>(this T obj, DataContractSerializer serializer)
{
var doc = new XDocument();
using (var writer = doc.CreateWriter())
{
(serializer ?? new DataContractSerializer(obj.GetType())).WriteObject(writer, obj);
}
return doc;
}
public static T DeserializeContract<T>(this XDocument doc)
{
return doc.DeserializeContract<T>(null);
}
public static T DeserializeContract<T>(this XDocument doc, DataContractSerializer serializer)
{
if (doc == null)
throw new ArgumentNullException();
using (var reader = doc.CreateReader())
{
return (T)(serializer ?? new DataContractSerializer(typeof(T))).ReadObject(reader);
}
}
}
Next, prune the undesired elements using XPATH queries, and then serialize the XDocument to a final XML representation.
Finally, if performance and memory use are at a premium, you could use the ElementSkippingXmlTextWriter from this answer to prune undesired elements as they are written.
This question already has answers here:
'POCO' definition
(11 answers)
Closed 8 years ago.
Can a POCO contain a constructor? Could you tell me if this class is correct? I also read that a POCO must have a parameterless constructor. Is that correct and why? If I accept this parameterless constructor, I'll have a problem with my read only Id property. For me, it seems logical that if this property must be read-only, then the only way to initialize it is in the constructor.
[DataContract]
public class MembershipUser
{
public MembershipUser(Guid idValue)
{
this.Id = idValue;
}
[DataMember]
public virtual readonly Guid Id { get; set; }
[DataMember]
public virtual string UserName { get; set; }
[DataMember]
public virtual string Email { get; set; }
}
A POCO object can contain other constructors but a default constructor is needed for deserialization as that process tries to create a default instance of the object and then sets the properties.
After some research it seems that the datacontract serializer does not use or even call the default constructor. Using this you could exclude a default ctor and still serialize your POCO. Whether or not you should is an implementation detail that you and your team would have to decide.
DataContractSerializer Tests:
[Test]
public void SerializerTest()
{
var s = new SerializeMe(2);
s.Name = "Test";
DataContractSerializer dcs = new DataContractSerializer(typeof(SerializeMe));
Stream ms = new MemoryStream();
var writer = XmlWriter.Create(ms);
Assert.DoesNotThrow(() => dcs.WriteObject(writer, s));
writer.Close();
ms.Position = 0;
var reader = new StreamReader(ms);
var xml = reader.ReadToEnd();
Console.WriteLine(xml);
}
[Test]
public void DeserializeTest()
{
var xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><SerializeMe xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://schemas.datacontract.org/2004/07/Test\"><Id>2</Id><Name>Test</Name></SerializeMe>";
DataContractSerializer dcs = new DataContractSerializer(typeof(SerializeMe));
XmlReader reader = XmlReader.Create(new StringReader(xml));
var obj = dcs.ReadObject(reader) as SerializeMe;
Assert.AreEqual(obj.Name, "Test");
}
[DataContract]
public class SerializeMe
{
public SerializeMe(int id)
{
this.Id = id;
}
[DataMember]
public int Id { get; private set; }
[DataMember]
public string Name { get; set; }
}
I got the following code:
public class Alarm:IDisposable
{
MemoryStream _tmp;
readonly XmlDocument _doc;
private readonly XmlSerializerNamespaces _ns;
private readonly XmlSerializer _x = new XmlSerializer(typeof(Alarm));
public int? ID { get; set; }
public string SourceSystem { get; set; }
public string SensorName { get; set; }
public string ModelName { get; set; }
public int? Severity { get; set; }
public int? Duration { get; set; }
public bool? Status { get; set; }
public DateTime? StartTime { get; set; }
public DateTime? EndTime { get; set; }
public Alarm()
{
_tmp = new MemoryStream();
_doc = new XmlDocument();
_ns = new XmlSerializerNamespaces();
_ns.Add("", "");
}
public string OuterXml()
{
//Add an empty namespace and empty value
_x.Serialize(_tmp, this, _ns);
_tmp.Position = 0;
_doc.Load(_tmp);
return _doc.OuterXml;
}
public void Dispose()
{
if (_tmp!=null)
{
_tmp.Close();
_tmp = null;
}
}
}
I get as output"
<?xml version=\"1.0\"?>
<Alarm><ID>1</ID><SourceSystem>HMer</SourceSystem>
<SensorName>4</SensorName><Severity d2p1:nil=\"true\" xmlns:d2p1=\"http://www.w3.org/2001/XMLSchema-instance\" />
<Duration>500</Duration><Status>true</Status>
<StartTime>2011-07-19T12:29:51.171875+03:00</StartTime>
<EndTime d2p1:nil=\"true\"
xmlns:d2p1=\"http://www.w3.org/2001/XMLSchema-instance\" /></Alarm>
I wanna get:
<Alarm><ID>1</ID><SourceSystem>HMer</SourceSystem><SensorName>4</SensorName>
<Duration>500</Duration><Status>true</Status>
<StartTime>2011-07-19T12:29:51.171875+03:00</StartTime></Alarm>
meaning no xmlns stuff, no tag where value is null.
please assist meh
Add the following properties to your Nullable fields:
[XmlIgnore]
public bool EndTimeSpecified { get { return EndTime.HasValue; } }
This lets XmlSerializer (which is a bit dumb about Nulllable fields) realize that no EndTime has been specified, and, thus, the field does not need to be serialized.
Here's the documentation of this feature:
Another option is to use a special pattern to create a Boolean field recognized by the XmlSerializer, and to apply the XmlIgnoreAttribute to the field. The pattern is created in the form of propertyNameSpecified. For example, if there is a field named "MyFirstName" you would also create a field named "MyFirstNameSpecified" that instructs the XmlSerializer whether to generate the XML element named "MyFirstName".
Here's a related SO question that offers more details and explanations:
How to make a value type nullable with .NET XmlSerializer?
About removing the XML processing instruction (i.e., the <?xml ... ?> part), there's also a SO question about that:
Omitting XML processing instruction when serializing an object
I'm converting a Dictionary object into a List<> derived class by means of the following two declarations:
[Serializable]
public class LogItem
{
public string Name { get; set; }
public string Value { get; set; }
public LogItem(string key, string value)
{
Name = key; Value = value;
}
public LogItem() { }
}
public class SerializableDictionary : List<LogItem>
{
public SerializableDictionary(Dictionary<string, string> table)
{
IDictionaryEnumerator index = table.GetEnumerator();
while (index.MoveNext())
{
Put(index.Key.ToString(), index.Value.ToString());
}
}
private void Put(string key, string value)
{
base.Add(new LogItem(key, value));
}
}
I intend to serialize SerializableDictionary by means of the following code:
SerializableDictionary log = new SerializableDictionary(contents);
using (StringWriter xmlText = new StringWriter())
{
XmlSerializer xmlFormat =
new XmlSerializer(typeof(SerializableDictionary), new XmlRootAttribute("Log"));
xmlFormat.Serialize(xmlText, log);
}
Works fine, but I'm unable to change the XML formatting.
This XML document is intended to be sent to a xml database field and is not meant to be deserialized.
I'd rather have both Name and Value
formatted as attributes of the
LogItem element.
However any attempts at using XMLAttribute have resulted in Reflection or compilation errors. I'm at loss as to what I can do to achieve this requirement. Could someone please help?
you can annotate them with XmlAttribute:
[Serializable]
public class LogItem
{
[XmlAttribute]
public string Name { get; set; }
[XmlAttribute]
public string Value { get; set; }
public LogItem(string key, string value)
{
Name = key; Value = value;
}
public LogItem() { }
}
This worked fine for me and produced the following XML (based on sample input):
<?xml version="1.0" encoding="utf-16"?>
<Log xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<LogItem Name="foo" Value="bar" />
<LogItem Name="asdf" Value="bcxcvxc" />
</Log>
There must be something else going on besides what you are showing. I am able to compile and execute the above code both with and without the XmlAttribute attribute applied to the Name and Value properties.
[XmlAttribute]
public string Name { get; set; }
[XmlAttribute]
public string Value { get; set; }