I'm doing a request to server, which returns the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<result><category>...
I am using the following code to deserialize:
private ListCategories DeserializeXML()
{
XmlSerializer xml = new XmlSerializer(typeof(ListCategories));
using (FileStream fs = new FileStream("CategoriesListXML.xml", FileMode.OpenOrCreate))
{
return (ListCategories)xml.Deserialize(fs);
}
}
And i have the following for my class Categories
[Serializable]
public class ListCategories
{
public List<Categories> CategoriesList { get; set; } = new List<Categories>();
}
[Serializable]
public class Categories
{
public int id { get; set; }
public int parent_id { get; set; }
public string name { get; set; }
public string image { get; set; }
public Categories() { }
public Categories(int id, int parent_id, string name, string image)
{
this.id = id;
this.parent_id = parent_id;
this.name = name;
this.image = image;
}
}
But when I deserialize the xml document I get a problem in this line:
return (ListCategories)xml.Deserialize(fs);
InvalidOperationException: <result xmlns=''> was not expected.
So can someone explain to me why the error is happening? As well as a possible solution?
Nothing in your defined model is result, so it makes sense that it is confused about what to do with <result>; the root element of ListCategories as defined is: <ListCategories>. If that isn't what you expect, you can use attributes to tell it what it should be, for example [XmlRoot("result")] on the class ListCategories. But: we'd need to see what you expect to advise on the exact attributes.
If I had to guess:
[XmlRoot("result")]
public class ListCategories
{
[XmlElement("category")]
public List<Categories> CategoriesList { get; set; } = new List<Categories>();
}
Note you can lose the [Serializable]. That has nothing to do with xml serialization. You can also probably lose the set on CategoriesList.
Related
I have the following XML;
<?xml version="1.0" encoding="UTF-8" ?>
<feedback>
<report_metadata>
<org_name>example.com</org_name>
</report_metadata>
</feedback>
and the following Feedback.cs class;
[XmlRoot("feedback", Namespace = "", IsNullable = false)]
public class Feedback
{
[XmlElement("report_metadata")]
public MetaData MetaData { get; set; }
}
[XmlType("report_metadata")]
public class MetaData
{
[XmlAttribute("org_name")]
public string Organisation { get; set; }
}
When I attempt to deserialize, the value for Organisation is null.
var xml = System.IO.File.ReadAllText("example.xml");
var serializer = new XmlSerializer(typeof(Feedback));
using (var reader = new StringReader(input))
{
var feedback = (Feedback)serializer.Deserialize(reader);
}
Yet, when I change Feedback.cs to the following, it works (obviously the property name has changed).
[XmlType("report_metadata")]
public class MetaData
{
//[XmlAttribute("org_name")]
public string org_name { get; set; }
}
I want the property to be Organisation, not org_name.
In the example XML file org_name is an XML element, not an XML attribute. Changing
[XmlAttribute("org_name")] to [XmlElement("org_name")] at the Organisation property will deserialize it as an element:
[XmlElement("org_name")]
public string Organisation { get; set; }
probably just typo
[XmlAttribute("org_name")]
public string Organisation { get; set; }
was supposed to be
[XmlElement("org_name")]
public string Organisation { get; set; }
Try to modify your Xml classes like
[XmlRoot(ElementName = "report_metadata")]
public class MetaData
{
[XmlElement(ElementName = "org_name")]
public string Organisation { get; set; }
}
[XmlRoot(ElementName = "feedback")]
public class Feedback
{
[XmlElement(ElementName = "report_metadata")]
public MetaData MetaData { get; set; }
}
Then you will get your desired output like
class Program
{
static void Main(string[] args)
{
Feedback feedback = new Feedback();
var xml = System.IO.File.ReadAllText(#"C:\Users\Nullplex6\source\repos\ConsoleApp4\ConsoleApp4\Files\XMLFile1.xml");
var serializer = new XmlSerializer(typeof(Feedback));
using (var reader = new StringReader(xml))
{
feedback = (Feedback)serializer.Deserialize(reader);
}
Console.WriteLine($"Organization: {feedback.MetaData.Organisation}");
Console.ReadLine();
}
}
Output:
I know there are several posts out there with this topic, but I can't seem to figure out what is the problem here. I have serialized and deserialized xml several times, and never had this error.
The exception message is:
There is an error in XML document (1, 2).
With InnerException:
<InvoiceChangeRequest xmlns=''> was not expected.
XML file I want to deserialize:
<ns1:InvoiceChangeRequest xmlns:ns1="http://kmd.dk/fie/external_invoiceDistribution">
<CONTROL_FIELDS>
<STRUCTURID>0000000001</STRUCTURID>
<OPERA>GET</OPERA>
<WIID>000050371220</WIID>
</CONTROL_FIELDS>
<HEADER_IN>
<MANDT>751</MANDT>
<BELNR>1234567890</BELNR>
</HEADER_IN>
<ITEMS>
<ITEM_FIELDS_IN>
<BUZEI>001</BUZEI>
<BUKRS>0020</BUKRS>
</ITEM_FIELDS_IN>
</ITEMS>
</ns1:InvoiceChangeRequest>
Class I'm trying to deserialize to:
[XmlRoot(Namespace = "http://kmd.dk/fie/external_invoiceDistribution", IsNullable = false)]
public class InvoiceChangeRequest
{
[XmlElement("CONTROL_FIELDS")] public ControlFields Styrefelter;
[XmlElement("HEADER_IN")] public HeaderIn HeaderfelterInd;
[XmlElement("ITEMS")] public Items Linjer;
}
public class HeaderIn
{
[XmlElement("MANDT")] public string Kommunenummer;
[XmlElement("BELNR")] public string RegnskabsbilagsNummer;
}
public class Items
{
[XmlElement("ITEM_FIELDS_IN")] public Itemfield[] ItemfelterInd;
}
public class Itemfield
{
[XmlElement("BUZEI")] public string Linjenummer;
[XmlElement("BUKRS")] public string Firmakode;
}
Deserialization code:
XmlSerializer serializer = new XmlSerializer(typeof(InvoiceChangeRequest));
var request = serializer.Deserialize(new StringReader(output)) as InvoiceChangeRequest;
In your XML file your root element is the namespace http://kmd.dk/fie/external_invoiceDistribution with prefix ns1.
The element <CONTROL_FIELDS> isn't because it isn't prefixed. Your serialization class doesn't take this into account though. That means that it expects that <CONTROL_FIELDS> and the other elements are ALSO in the ns1 namespace.
To get the serializer parse the elements correctly add the Namespace to the elements, setting it to an empty string:
[XmlRoot(Namespace = "http://kmd.dk/fie/external_invoiceDistribution", IsNullable = false)]
public class InvoiceChangeRequest
{
[XmlElement("CONTROL_FIELDS", Namespace = "")]
public ControlFields Styrefelter { get; set; }
[XmlElement("HEADER_IN", Namespace = "")]
public HeaderIn HeaderfelterInd { get; set; }
[XmlElement("ITEMS", Namespace = "")]
public Items Linjer { get; set; }
}
This will de-serialize the given XML as intended.
In case of de-serialization issues I often create the classes in memory and then serialize that so I can inspect the resulting XML. That often gives clues on what is missing or being added compared to the input document:
var ms = new MemoryStream();
serializer.Serialize(ms, new InvoiceChangeRequest {
Styrefelter = new ControlFields { Opera="test"}
});
var s = Encoding.UTF8.GetString(ms.ToArray());
And then inspect s for differences.
You can replace 'ns1:' with string.Empty.
Below classes should serialize.
public class Item
{
[XmlElement("BUZEI")]
public string Buzei { get; set; }
[XmlElement("BUKRS")]
public string Bukrs { get; set; }
}
public class Header
{
[XmlElement("MANDT")]
public string Mandt { get; set; }
[XmlElement("BELNR")]
public string Belnr { get; set; }
}
public class ControlFields
{
[XmlElement("STRUCTURID")]
public string StructuredId { get; set; }
[XmlElement("OPERA")]
public string Opera { get; set; }
[XmlElement("WIID")]
public string Wild { get; set; }
}
public class InvoiceChangeRequest
{
[XmlElement("CONTROL_FIELDS")]
public ControlFields ControlFields { get; set; }
[XmlElement("HEADER_IN")]
public Header Header { get; set; }
[XmlArray("ITEMS")]
[XmlArrayItem("ITEM_FIELDS_IN")]
public List<Item> Items { get; set; }
}
My XML:
<result>
<document version="2.1.0">
<response type="currency">
<currency>
<code>AMD</code>
<price>85.1366</price>
</currency>
</response>
<response type="currency">
<currency>
<code>AUD</code>
<price>31.1207</price>
</currency>
</response>
</document>
</result>
My Class:
public class CurrencyData
{
public string Code { get; set; }
public string Price { get; set; }
}
My deserializer calling:
RestClient.ExecuteAsync<List<CurrencyData>>...
If i renamed class CurrencyData to Currency then all will been done right. But I want to keep this class name.
Ok, I think I got it,
You can try RestClient.ExecuteAsync<Result>()
[XmlRoot("result")]
public class Result
{
[XmlElement("document")]
public Document Document { get; set; }
}
public class Document
{
[XmlElement("response")]
public Response[] Responses { get; set; }
[XmlAttribute("version")]
public string Version { get; set; }
}
public class Response
{
[XmlElement("currency")]
public CurrencyData Currency { get; set; }
[XmlAttribute("type")]
public string Type { get; set; }
}
public class CurrencyData
{
[XmlElement("code")]
public string Code { get; set; }
[XmlElement("price")]
public decimal Price { get; set; }
}
I had to add a few XmlElement attribute to override the casing without having to name classes and properties in lowercase. but you can drop them if you can change the xml to match the casing
Answer of kay.one is perfect! It works with a any remarks:
public List<Response> Responses { get; set; }
works
public Response[] Responses { get; set; }
don`t works
And
[XmlElement("AnyValue")]
it is from System.Xml.Serialization namespace and don`t work.
Feel free to just delete them.
In this example annotation attributes and properties has same names and serializer understands.
But right annotation attributes are from RestSharp.Deserializers namespace
[DeserializeAs(Name="AnyXmlValue")]
public string AnyModelValue { get; set; }
How to manage deserialization of RestSharp
Then change the xml tag to CurrencyData. Here is the documentation about the xml deserializer: https://github.com/restsharp/RestSharp/wiki/Deserialization
I'm not sure why Kay.one's answer is accepted, it doesn't answer the question.
Per my comment, the default RestSharp deserializer doesn't check for the DeserializeAs attribute when deserializing a list. I'm not sure if that's intentional or a mistake as the author doesn't seem to be very available.
Anyways it's a simple fix.
private object HandleListDerivative(object x, XElement root, string propName, Type type)
{
Type t;
if (type.IsGenericType)
{
t = type.GetGenericArguments()[0];
}
else
{
t = type.BaseType.GetGenericArguments()[0];
}
var list = (IList)Activator.CreateInstance(type);
var elements = root.Descendants(t.Name.AsNamespaced(Namespace));
var name = t.Name;
//add the following
var attribute = t.GetAttribute<DeserializeAsAttribute>();
if (attribute != null)
name = attribute.Name;
I've managed to deserialize my XML for the most part, but I'm stuck on one particular thing. How do I get the value of an element, if it's in an array, and each item in that array has it's own attributes. Let me show you what I have
<BusinessObject Name="BusinessName" RecID="12345">
<FieldList>
<Field Name="Field1">FieldValue1</Field>
<Field Name="Field2">FieldValue2</Field>
</FieldList>
</BusinessObject>
So this is a cut-down version, but shows the basis of the XML. I'm currently having trouble trying to capture "FieldValue1" and "FieldValue2" in their respective Field elements.
[XmlRoot("BusinessObject")]
public sealed class BusinessObject
{
[XmlAttribute("Name")]
public string Name { get; set; }
[XmlAttribute("RecID")]
public string RecID { get; set; }
[XmlElement("FieldList", Type = typeof(FieldList))]
public FieldList FieldList { get; set; }
public BusinessObject()
{
FieldList = null;
}
public static BusinessObject FromXmlString(string xmlString)
{
var reader = new StringReader(xmlString);
var serializer = new XmlSerializer(typeof(BusinessObject));
var instance = (BusinessObject)serializer.Deserialize(reader);
return instance;
}
}
[Serializable]
public class FieldList
{
[XmlElement("Field", Type = typeof(Field))]
public Field[] Fields { get; set; }
public FieldList()
{
Fields = null;
}
}
[Serializable]
public class Field
{
[XmlAttribute("Name")]
public string Name { get; set; }
public Field()
{
}
}
I'm sure it's just something simple that I'm missing, but I was having trouble trying to put my problem into words to perform a relevant search.
Many thanks,
Mark
Thanks for taking a look. Clearly I should play around more before posting here, as I've just managed to get it working. I had to add the following to the Field class:
[Serializable]
public class Field
{
[XmlAttribute("Name")]
public string Name { get; set; }
[XmlText]
public string Value { get; set; }
public Field()
{
}
}
Unfortunately I'm just running on a copy of express here, so I couldn't work out how to use xsd without the command prompt.
Cheers all,
Mark
As the title says when i deserialize the following file i only get the first (and always the first) element:
<?xml version="1.0"?>
<ServerConnections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Entries>
<ServerConnectionEntry>
<Name>Local</Name>
<Host>127.0.0.1</Host>
<Port>15556</Port>
<Username>TestUser</Username>
<AuthHash>
<base64Binary>u7a0NN4uOvCrb5t5UWVVEl14Ygo=</base64Binary>
</AuthHash>
</ServerConnectionEntry>
<ServerConnectionEntry>
<Name>Local2</Name>
<Host>127.0.0.1</Host>
<Port>15556</Port>
<Username>TestUser</Username>
<AuthHash>
<base64Binary>u7a0NN4uOvCrb5t5UWVVEl14Ygo=</base64Binary>
</AuthHash>
</ServerConnectionEntry>
</Entries>
</ServerConnections>
My code to deserialize:
var list = (ServerConnections)mSerializer.Deserialize(inputStream)).Entries;
and list.Count then is 1. ServerConnections looks like that:
public class ServerConnections
{
public ServerConnectionEntry[] Entries { get; set; }
}
There is no exception happening.
Edit:
The problem occurs when I include my class that does custom xml serialization (implements IXmlSerializable). What it does is the following:
void BigNumber::ReadXml(System::Xml::XmlReader^ reader) {
reader->ReadStartElement();
XmlSerializer^ serializer = gcnew XmlSerializer(cli::array<Byte>::typeid);
cli::array<Byte>^ data = (cli::array<Byte>^)serializer->Deserialize(reader);
pin_ptr<unsigned char> ptr(&data[0]);
BN_bin2bn(ptr, data->Length, mNumber);
}
void BigNumber::WriteXml(System::Xml::XmlWriter^ writer) {
XmlSerializer^ serializer = gcnew XmlSerializer(cli::array<Byte>::typeid);
serializer->Serialize(writer, ToByteArray());
}
While data contains the correct data after ReadXml the deserializer that works the whole list stops and does not read any additional elements.
Same here, this seems to work fine for me using code similar to yours.
public class Program
{
static void Main(string[] args)
{
XmlSerializer deserializer = new XmlSerializer(typeof(ServerConnections));
var reader = new StreamReader(#"../../Test.xml");
var entries = (ServerConnections)deserializer.Deserialize(reader);
reader.Close();
}
public class ServerConnections
{
public ServerConnectionEntry[] Entries { get; set; }
}
public class ServerConnectionEntry
{
public string Name { get; set; }
public string Host { get; set; }
public string Port { get; set; }
public string Username { get; set; }
public BinaryCode AuthHash { get; set; }
}
public class BinaryCode
{
[XmlElement("base64Binary")]
public string Code { get; set; }
}
}
I do not see any issue. I've even reproduced your scenario (complete test enclosed below) of your code, and it is doing its job correctly.
Try to search elsewhere (e.g. assure that the passed xml is the one you are expecting). But serialization is working correctly with your C# class mapping
EDIT: AuthHash class no does the conversion for you from byte[] to base64 and back
public class ServerConnections
{
public ServerConnectionEntry[] Entries { get; set; }
}
public class ServerConnectionEntry
{
public string Name { get; set; }
public AuthHash AuthHash { get; set; }
}
public class AuthHash
{
[XmlIgnore]
public byte[] Hash { get; set; }
public string base64Binary
{
get { return Convert.ToBase64String(Hash); }
set { Hash = Convert.FromBase64String(value); }
}
}
[TestClass]
public class DeserializationTest
{
public const string MyXml = #"<?xml version=""1.0""?>
<ServerConnections
xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
<Entries>
<ServerConnectionEntry>
<Name>Local</Name>
<Host>127.0.0.1</Host>
<Port>15556</Port>
<Username>TestUser</Username>
<AuthHash>
<base64Binary>u7a0NN4uOvCrb5t5UWVVEl14Ygo=</base64Binary>
</AuthHash>
</ServerConnectionEntry>
<ServerConnectionEntry>
<Name>Local2</Name>
<Host>127.0.0.1</Host>
<Port>15556</Port>
<Username>TestUser</Username>
<AuthHash>
<base64Binary>u7a0NN4uOvCrb5t5UWVVEl14Ygo=</base64Binary>
</AuthHash>
</ServerConnectionEntry>
</Entries>
</ServerConnections>
";
[TestMethod]
public void Deserialization_Has_Two_Elements()
{
TextReader reader = new StringReader(MyXml);
var mySerializer = new XmlSerializer(typeof(ServerConnections));
var list = ((ServerConnections)mySerializer.Deserialize(reader)).Entries;
Assert.IsTrue(list.Count() == 2);
Assert.IsTrue(list.First().Name == "Local");
Assert.IsTrue(list.Last().Name == "Local2");
Assert.IsTrue(list.First().AuthHash.Hash.Length > 0);
Assert.IsTrue(list.Last().AuthHash.Hash.Length > 0);
}
}
Well fizzlesticks, the problem was that i forgot one tiny little line in the deserialization. It should be like that:
void BigNumber::ReadXml(System::Xml::XmlReader^ reader) {
reader->ReadStartElement();
XmlSerializer^ serializer = gcnew XmlSerializer(cli::array<Byte>::typeid);
cli::array<Byte>^ data = (cli::array<Byte>^)serializer->Deserialize(reader);
pin_ptr<unsigned char> ptr(&data[0]);
BN_bin2bn(ptr, data->Length, mNumber);
reader->ReadEndElement();
}
The ReadEndElement makes sure it advances to the next node. As i didn't do that the deserializer above had a problem but instead of throwing an exception it just stops parsing and returns what it got so far...