I am having real trouble trying to deserialize some XML and was hoping someone can offer some assistance. I have read a lot of similar posts but I am unable to resolve this.
XML I am attempting to deserialize
<register-account success="false">
<user-name>xxxxx</user-name>
<password>fghgh</password>
<email>test#example.com</email>
<error>
<errorcode>120</errorcode>
<errormessage>The password is invalid</errormessage>
</error>
</register-account>
Class I am trying to deserialize to:
[Serializable, XmlRoot(ElementName = "register-account", Namespace = "MyNamespace")]
[XmlType("register-account")]
public class RegisterAccountResponse
{
[XmlAttribute("success")]
public bool Success { get; set; }
/// <summary>
/// Gets or sets the Tennant email address
/// </summary>
[XmlElement("email")]
public string Email { get; set; }
/// <summary>
/// Gets or sets the tennant password
/// </summary>
[XmlElement("password")]
public string Password { get; set; }
/// <summary>
/// Gets or sets the Tennant username
/// </summary>
[XmlElement("user-name")]
public string Username { get; set; }
/// <summary>
/// A Tenant Portal error relating to the RegisterAccountRequest
/// </summary>
[XmlElement("error")]
public QubeError Error;
}
Deserialization Method
public static T Deserialize<T>(string data) where T : class
{
if (data == null)
{
return null;
}
if (data.Trim().Length == 0)
{
return null;
}
var ser = new XmlSerializer(typeof(T));
using (var sr = new StringReader(data))
{
return (T)ser.Deserialize(sr);
}
}
Deserialization Method Call
var data = Helper.Deserialize<RegisterAccountResponse>(xml);
Exception:
There is an error in XML document (1,
2). --->
System.InvalidOperationException: was
not expected. at
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderData.Read5_data()
Inner Exception as follows:
<register-account xmlns=''> was not expected.
Simply take off the Namespace =:
[XmlRoot("register-account"), XmlType("register-account")]
public class RegisterAccountResponse {...}
since your xml doesn't seem to be in an xml-namespace. Also, [Serializable] isn't used by XmlSerializer.
If your xml was using a namespace it would have an xmlns at the root.
Also, to help with callers you could add where T : class, new() (the , new() being the addition) to your Deserialize method, since XmlSerializer demands a public parameterless constructor.
Nothing worked here for me
What worked is to MAKE SURE that the C# Class (main class) you are trying to map/deserialize the xml string to HAS AN XmlRootAttribute that matches the root element of the response.
Check my full answer with an exmaple https://stackoverflow.com/a/61525536/1594274
I found doing the following fixed this for me
if (elem.Attribute(XNamespace.Xmlns + "xsi") == null) {
elem.Add(new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"));
}
if (elem.Attribute(XNamespace.Xmlns + "xsd") == null) {
elem.Add(new XAttribute(XNamespace.Xmlns + "xsd", "http://www.w3.org/2001/XMLSchema"));
}
Related
I am having real trouble trying to deserialize some XML and was hoping someone can offer some assistance. I have read a lot of similar posts but I am unable to resolve this.
XML I am attempting to deserialize
<register-account success="false">
<user-name>xxxxx</user-name>
<password>fghgh</password>
<email>test#example.com</email>
<error>
<errorcode>120</errorcode>
<errormessage>The password is invalid</errormessage>
</error>
</register-account>
Class I am trying to deserialize to:
[Serializable, XmlRoot(ElementName = "register-account", Namespace = "MyNamespace")]
[XmlType("register-account")]
public class RegisterAccountResponse
{
[XmlAttribute("success")]
public bool Success { get; set; }
/// <summary>
/// Gets or sets the Tennant email address
/// </summary>
[XmlElement("email")]
public string Email { get; set; }
/// <summary>
/// Gets or sets the tennant password
/// </summary>
[XmlElement("password")]
public string Password { get; set; }
/// <summary>
/// Gets or sets the Tennant username
/// </summary>
[XmlElement("user-name")]
public string Username { get; set; }
/// <summary>
/// A Tenant Portal error relating to the RegisterAccountRequest
/// </summary>
[XmlElement("error")]
public QubeError Error;
}
Deserialization Method
public static T Deserialize<T>(string data) where T : class
{
if (data == null)
{
return null;
}
if (data.Trim().Length == 0)
{
return null;
}
var ser = new XmlSerializer(typeof(T));
using (var sr = new StringReader(data))
{
return (T)ser.Deserialize(sr);
}
}
Deserialization Method Call
var data = Helper.Deserialize<RegisterAccountResponse>(xml);
Exception:
There is an error in XML document (1,
2). --->
System.InvalidOperationException: was
not expected. at
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderData.Read5_data()
Inner Exception as follows:
<register-account xmlns=''> was not expected.
Simply take off the Namespace =:
[XmlRoot("register-account"), XmlType("register-account")]
public class RegisterAccountResponse {...}
since your xml doesn't seem to be in an xml-namespace. Also, [Serializable] isn't used by XmlSerializer.
If your xml was using a namespace it would have an xmlns at the root.
Also, to help with callers you could add where T : class, new() (the , new() being the addition) to your Deserialize method, since XmlSerializer demands a public parameterless constructor.
Nothing worked here for me
What worked is to MAKE SURE that the C# Class (main class) you are trying to map/deserialize the xml string to HAS AN XmlRootAttribute that matches the root element of the response.
Check my full answer with an exmaple https://stackoverflow.com/a/61525536/1594274
I found doing the following fixed this for me
if (elem.Attribute(XNamespace.Xmlns + "xsi") == null) {
elem.Add(new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"));
}
if (elem.Attribute(XNamespace.Xmlns + "xsd") == null) {
elem.Add(new XAttribute(XNamespace.Xmlns + "xsd", "http://www.w3.org/2001/XMLSchema"));
}
When serializing my object with Newtonsoft's Json.net I get:
{"status":"1",
"message":"test",
"records":"[{\"id\":\"1\", \"name\":\"file1\"},
{\"id\":\"2\", \"name\":\"file2\"},
{\"id\":\"3\", \"name\":\"file3\"}]" // I want to get rid of the extra quotes for the array
}
I want to have:
{"status":"1",
"message":"test",
"records":[{"id":"1", "name":"file1"},
{"id":"2", "name":"file2"},
{"id":"3", "name":"file3"}] // NOTE: this is an Array of records
}
This is the simplified code I use to serialize:
QHttpResponse tempResponse = new QHttpResponse() { StatusCode = (int)HttpStatusCode.OK, Message = "File found." };
JObject jo = JObject.FromObject(tempResponse);
jo.Add("records",JsonConvert.SerializeObject(jo));
This is the QHttpResponse class:
public class QHttpResponse
{
#region Feilds
/// <summary>
/// Status code of the http response (e.g.:400 = bad request.)
/// </summary>
[JsonProperty("status_code")]
public int StatusCode { get; set; }
/// <summary>
/// Message (Content) of the response.
/// </summary>
[JsonProperty("message")]
public string Message { get; set; }
#endregion
}
Your problem is here:
jo.Add("records",JsonConvert.SerializeObject(jo));
You serialize the array adding it to the "records" property, then you serialized the whole thing thus you get a double serialization, which is why you have the escaped \".
try:
jo["records"] = arrayData;
Then later when you serialize this should come out as you expect.
In a nutshell:
I have a list with items named Categories. For each of those items in the list Categories I have a xml file:
System.IO.File.Create(Categories[listPicker.SelectedIndex] + ".xml");
The created item is serialized into a xml file with the name of the selected index.
The problem:
The problem is that for each item in categories an xml file needs to be deserialized to a list, because each object of categories must be another list because it also contains items.
But there does not exist a list for deserialization of the xml file:
Deserialization:
Serialize.Deserialize(Variable list name , Categories[1]+".xml");
So how do i dynamically create lists or can you provide a better solution to this problem?
Even if each list has a different name, the structure of the content is the same. Text/Description and Value.
I would create a class for the Item. Something like
public class ComboItem{
public string Value {get; set;}
public string Text {get; set;}
}
Then I would create another class for the List of ComboItem that each XML File would have
public class ComboItemList
{
public ComboItemList()
{
ComboItems = new List<ComboItem>();
}
[XmlArray("ComboItems"), XmlArrayItem("ComboItem")]
public List<ComboItem> ComboItems { get; set; }
}
I have a Serialize/Deserialize Helper. Something like
#region static T DeserializeObject( string xml, Encoding encoding )
/// <summary>
/// Deserialize an Xml String to an [object]
/// </summary>
/// <typeparam name="T">Object Type to Deserialize</typeparam>
/// <param name="xml">Xml String to Deserialize</param>
/// <param name="encoding">System.Text.Encoding Type</param>
/// <returns>Default if Exception, Deserialize object if successful</returns>
/// <example>
/// // UTF-16 Deserialize
/// [object] = SerializationHelper<ObjectType>DeserializeObject( xml, Encoding.Unicode )
/// </example>
/// <example>
/// // UTF-8 Deserialize
/// [object] = SerializationHelper<ObjectType>DeserializeObject( xml, Encoding.UTF8 )
/// </example>
public static T DeserializeObject(string xml, Encoding encoding)
{
if (string.IsNullOrEmpty(xml)) { return default(T); }
try
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (MemoryStream memoryStream = new MemoryStream(encoding.GetBytes(xml)))
{
// No settings need modifying here
XmlReaderSettings xmlReaderSettings = new XmlReaderSettings();
using (XmlReader xmlReader = XmlReader.Create(memoryStream, xmlReaderSettings))
{
return (T)xmlSerializer.Deserialize(xmlReader);
}
}
}
catch
{
return default(T);
}
}
#endregion
/// <summary>
/// Deserialize an Xml String to an [object] with UTF8 as Encoding
/// </summary>
/// <param name="xml">Xml String to Deserialize</param>
/// <returns>Default if Exception, Deserialize object if successful</returns>
/// <example>
/// [object] = SerializationHelper<ObjectType>DeserializeObject( xml )
/// </example>
public static T DeserializeObjectFromFile(string filePath)
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException(string.Format("The file {0}, don't exist.", filePath));
}
StreamReader sr = File.OpenText(filePath);
string xml = sr.ReadToEnd();
return DeserializeObject(xml, Encoding.UTF8);
}
that I would call like this
string filePath01 = "PATH TO FILE 01";
List<ComboItem> List01 = SerializationHelper<ComboItem>.DeserializeObjectFromFile(filePath01);
string filePath02 = "PATH TO FILE 02";
List<ComboItem> List02 = SerializationHelper<ComboItem>.DeserializeObjectFromFile(filePath02);
List01 and List02 would have the contents of File01 and File02. Repeat for every file you have.
With a better architecture, you could make some properties in base class, that could be singleton, and would only load each file, only once, increasing the performance.
I have the following ExportMetaData attributes set on my class:
[Export(typeof(IDocumentViewer))]
[ExportMetadata("Name", "MyViewer")]
[ExportMetadata("SupportsEditing", true)]
[ExportMetadata("Formats", DocFormat.DOC, IsMultiple = true)]
[ExportMetadata("Formats", DocFormat.DOCX, IsMultiple = true)]
[ExportMetadata("Formats", DocFormat.RTF, IsMultiple = true)]
I also have a supporting interface:
public interface IDocumentViewerMetaData {
/// <summary>
/// Gets the format.
/// </summary>
/// <value>The format.</value>
IEnumerable<DocFormat> Formats { get; }
/// <summary>
/// Gets the name of the viewer
/// </summary>
/// <value>The name.</value>
string Name { get; }
/// <summary>
/// Gets a value indicating whether this viewer supports editing
/// </summary>
/// <value><c>true</c> if [supports editing]; otherwise, <c>false</c>.</value>
bool SupportsEditing { get; }
}
And of course my ImportMany:
[ImportMany(typeof(IDocumentViewer))]
public IEnumerable<Lazy<IDocumentViewer, IDocumentViewerMetaData>> _viewers { get; set; }
What I would like to do is use a strongly-typed attribute class instead of using the ExportMetaData attribute. I have not figured out a way to do this while also supporting single values (Name, SupportsEditing, in the example above).
I envision doing something similiar the following (or whatever is suggested as best):
[Export(typeof(IDocumentViewer))]
[DocumentViewerMetadata(Name = "MyViewer")]
[DocumentViewerMetadata(SupportsEditing = true)]
[DocumentViewerMetadata(Format = DocFormat.DOC)]
[DocumentViewerMetadata(Format = DocFormat.DOCX)]
I am fairly certain that there IS a way to do this, I just haven't found the right way to connect the dots. :)
You can subclass the ExportAttribute with your own implementation, and decorate it with a MetadataAttribute to allow MEF to use its properties to project the metadata proxy it uses during composition:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property),
MetadataAttribute]
public class ExportDocumentViewerAttribute : ExportAttribute, IDocumentViewerMetadata
{
public ExportDocumentViewer(string name, bool supportsEditing, params DocFormat[] formats)
: base(typeof(IDocumentViewer))
{
if (string.IsNullOrEmpty(name))
throw new ArgumentException("Export requires a name", "name");
Name = name;
SupportsEditing = supportsEditing;
Formats = formats ?? Enumerable.Empty<DocFormat>();
}
public string Name { get; private set; }
public bool SupportsEditing { get; private set; }
public IEnumerable<DocFormat> Formats { get; private set; }
}
[ExportDocumentViewer("Word", true, DocFormat.DOC, DocFormat.DOCX)]
public WordDocumentViewer : IDocumentViewer
{
// Stuff
}
Note you don't actually need to decorate it with your IDocumentViewerMetadata contract, as MEF will project it regardless, I just prefer to so that I know if I make changes to the metadata contract, that my custom export attribute conforms.
I'm new to creating WCF REST services - so please let me kwow if I'm doing things wrong here.
I have a REST-based service developed using WCF, with the following DataContract defined:
namespace Messaging
{
[DataContract(Name = "Email", Namespace = "")]
public class Email
{
#region Fields
private string subject;
private string message;
private string address;
private string firstName;
private string lastName;
private string notifyWindowEnd;
private string eventNotificationID;
#endregion
#region Properties
/// <summary>
/// Email Subject.
/// </summary>
[DataMember(Name = "Subject", Order = 1)]
public string Subject
{
get { return subject; }
set { subject = value; }
}
/// <summary>
/// Email Body.
/// </summary>
[DataMember(Name = "Message", Order = 2)]
public string Message
{
get { return message; }
set { message = value; }
}
/// <summary>
/// Email Address of Recipient.
/// </summary>
[DataMember(Name = "Address", Order = 3)]
public string Address
{
get { return address; }
set { address = value; }
}
/// <summary>
/// First Name of Recipient.
/// </summary>
[DataMember(Name = "FirstName", Order = 4)]
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
/// <summary>
/// Last Name of Recipient.
/// </summary>
[DataMember(Name = "LastName", Order = 5)]
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
/// <summary>
/// Time at which the Email will cease to be sent.
/// </summary>
[DataMember(Name = "NotifyWindowEnd", Order = 6)]
public string NotifyWindowEnd
{
get { return notifyWindowEnd; }
set { notifyWindowEnd = value; }
}
/// <summary>
/// ID of the Event for which the Email has been generated.
/// </summary>
[DataMember(Name = "EventID", Order = 7)]
public string EventID
{
get { return eventID; }
set { eventID = value; }
}
#endregion
}
}
On the client application, I am trying to POST to a service (that uses this Data Contract) using the HttpWebRequest object with the output generated by a DataContractSerializer(typeof(MessagingWS.Email)) (since I have the Email class available to me after adding the WCF service using 'Add Service Reference...' as MessagingWS on the client application). In any event, this produces the following output which generates a 400 - Bad Request coming back from the service when I try to POST:
<email xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/MessagingWS">
<propertychanged xmlns:a="http://schemas.datacontract.org/2004/07/System.ComponentModel" i:nil="true">
<addressfield>first.last#gmail.com</addressfield>
<eventidfield>123456</eventidfield>
<firstnamefield>First</firstnamefield>
<lastnamefield>Last</lastnamefield>
<messagefield>Message Content</messagefield>
<notifywindowendfield>DateTime value</notifywindowendfield>
<subjectfield>Test Subject</subjectfield>
</propertychanged>
So I end up with all elements appended with "field", and they are not in the order I specified in the DataContract (they assume default order). Could someone please tell me what is going on here, and if I'm trying to accomplish something that isn't possible?
I did notice that when I copied the DataContract class from the server to the client (and included it under a different namespace - Messaging2), the output was serialized as expected when I used a DataContractSerializer(typeof(Messaging2.Email)). Is this the way I really should be doing it? It seems to work, but I figure I should be able to use the class that I get on the client side after running "Add Service Reference...".
Any help is greatly appreciated - thanks!
Your DataContract attribute says Namespace="" but your example XML says xmlns="http://schemas.datacontract.org/2004/07/MessagingWS".
Try regenerating your service reference.
What does the Email class definition imported by Add Service Reference look like on the client side? If I had to guess, I'd say it's probably wrong (for whatever reason). One thing I don't understand, though... are you sure the XML you show is actually the one generated by the DataContractSerializer in this case? It doesn't quite make sense to me... where does the element come from, since it's not defined in your class above?