C# XML Deserialize Array Element Values - c#

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

Related

XML Deserialization error: xxxxx was not expected

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; }
}

JavaScriptSerializer.Deserialize Gives Null Fields

I sent the following JSON to a WCF service which accepts Stream as a parameter.
{"ReservationStream":
{"clientFirstName":"Abe",
"clientLastName":"Lincoln",
"clientPhone":"0544944860",
"clientEmail":"abe#mail.com",
"pickupLocationID":"3699",
"pickupAddressString":"JFK Airport Terminal 1",
"pickupFlightNumber":"LY001",
"pickupAirline":"El Al",
"pickupAirportName":"John F Kennedy Intl",
}
}
In the debugger, the stream variable shows all values as expected. I want to deserialize into a class defined as follows:
public class ReservationStream
{
public String clientFirstName { get; set; }
public String clientLastName { get; set; }
public String clientPhone { get; set; }
public String clientEmail { get; set; }
public String pickupLocationID { get; set; }
public String pickupAddressString { get; set; }
public String pickupFlightNumber { get; set; }
public String pickupAirline { get; set; }
public String pickupAirportName { get; set; }
}
When I call
JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
ReservationStream structuredReservations = javaScriptSerializer.Deserialize<ReservationStream>(strJSON);
no exception is thrown, but all of the fields have null values. Where is my mistake?
I think it's because your JSON object is nested inside a ReservationStream tag.
Try with just:
{
"clientFirstName":"Abe",
"clientLastName":"Lincoln",
"clientPhone":"0544944860",
"clientEmail":"abe#mail.com",
"pickupLocationID":"3699",
"pickupAddressString":"JFK Airport Terminal 1",
"pickupFlightNumber":"LY001",
"pickupAirline":"El Al",
"pickupAirportName":"John F Kennedy Intl"
}
You're trying to deserialize an object containing a ReservationStream object rather than the ReservationStream object itself.
One way to get around this is to make a wrapper class which contains a ReservationStream and deserialize using this:
public class WrapperClass
{
public ReservationStream ReservationStream { get; set; }
}
WrapperClass wrapperClass = serializer.Deserialize<WrapperClass>(strJSON);
I once have the same problem and I removed the javaScriptSerializer and directly used the method parameter like this
public static string UpdateTemplateProperties(TemplateProperties templateProperties)
{
var x = templateProperties.Something
}
and I have the javascript object structure as you
just be sure to have the same name for the parameter and the first object-name in your javascript object literal

Populate custom List sub class from XML document via LINQ

I have figured out how to populate a custom class from XML data, but I ran into an issue along the way. Things were working perfectly with my existing method of populating data until I was thrown a bit of a curve ball. The new schema I was sent is similar to this:
<ITEM_REPLY>
<TRAN_ID>1320691307345</TRAN_ID>
<REPLY_CODE>0</REPLY_CODE>
<UNIT_PRICE>8.2784</UNIT_PRICE>
<SUP_LOCS>
<SUP_LOC>
<SUP_LOC_ID>001134</SUP_LOC_ID>
<COUNTRY_ID>USA</COUNTRY_ID>
<QTY_AVL>47.000</QTY_AVL>
<ITEM_UOM>EA</ITEM_UOM>
</SUP_LOC>
<SUP_LOC>
<SUP_LOC_ID>006817</SUP_LOC_ID>
<COUNTRY_ID>USA</COUNTRY_ID>
<QTY_AVL>20.000</QTY_AVL>
<ITEM_UOM>EA</ITEM_UOM>
</SUP_LOC>
</SUP_LOCS>
<MESSAGE />
<QTY_BREAKS />
</ITEM_REPLY>
Pretty standard XML schema, problem is I'm not sure how to populate my custom class with it. Here's what I do have:
static void Main(string[] args)
{
var order = ConvertXMLMessage<ItemReply>(request);
}
protected static T ConvertXMLMessage<T>(String xmlData) where T : class, new()
{
var xml = new XmlDocument();
xml.LoadXml(xmlData);
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
using (var xmlReader = new XmlNodeReader(xml.DocumentElement))
{
T work = (T)(serializer.Deserialize(xmlReader));
return work;
}
}
public class ItemReply
{
[XmlElement("ITEM_REPLY")]
public ItemAvlReply ITEM_REPLY { get; set; }
}
public class ItemAvlReply
{
[XmlElement("TRAN_ID")]
public string TRAN_ID { get; set; }
[XmlElement("REPLY_CODE")]
public string REPLY_CODE { get; set; }
[XmlElement("UNIT_PRICE")]
public string UNIT_PRICE { get; set; }
[XmlElement("SUP_LOCS")]
public SupplierLocations SUP_LOCS;
[XmlElement("MESSAGE")]
public string MESSAGE { get; set; }
[XmlElement("QTY_BREAKS")]
public string QTY_BREAKS { get; set; }
}
public class SupplierLocations
{
[XmlElement("SUP_LOC")]
public List<SupplierLocation> SUP_LOC;
}
public class SupplierLocation
{
[XmlElement("SUP_LOC_ID")]
public string SUP_LOC_ID { get; set; }
[XmlElement("COUNTRY_ID")]
public string COUNTRY_ID { get; set; }
[XmlElement("QTY_AVL")]
public string QTY_AVL { get; set; }
[XmlElement("ITEM_UOM")]
public string ITEM_UOM { get; set; }
}
This works perfectly minus the List<Item> part. I'm not overly experienced with LINQ and I'm not sure how to go about declaring a sub array in my class via this statement. I am also open to a different approach from creating the List<Item> part, I'm just not sure where to start otherwise. Is there a better approach for what I'm need to do? Is there an easy solution I am just unaware of in LINQ?
Here's a simple way to do it, assuming the example XML file you provided has typos. I assumed the OrderId has a closing tag, and that the closing tag for Items should be /Items.
Here's the version of the xml I used:
<Order>
<TransactionID>123</TransactionID>
<OrderID>1</OrderID>
<Items Number="2">
<Item>
<ItemName>Test</ItemName>
<Color>Red</Color>
</Item>
<Item>
<ItemName>Test1</ItemName>
<Color>Blue</Color>
</Item>
</Items>
</Order>
Here's the code to read/write the XML: (the xml variable is a String)
var order = ConvertXMLMessage<Order>(xml);
WriteXMLFile<Order>(order, #"test.xml");
Here's the ConvertXMLMessage and WriteXMLFile functions:
protected static T ConvertXMLMessage<T>(String xmlData) where T : class, new()
{
var xml = new XmlDocument();
xml.LoadXml(xmlData);
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
using (var xmlReader = new XmlNodeReader(xml.DocumentElement))
{
T work = (T)(serializer.Deserialize(xmlReader));
return work;
}
}
protected static void WriteXMLFile<T>(T item, String saveLocation) where T : class, new()
{
System.Xml.Serialization.XmlSerializer writer = new System.Xml.Serialization.XmlSerializer(typeof(T));
System.IO.StreamWriter file = new System.IO.StreamWriter(saveLocation);
writer.Serialize(file, item);
file.Close();
}
and here's the class structure:
public class Order
{
[XmlElement("TransactionID")]
public string TransactionId { get; set; }
[XmlElement("OrderID")]
public string OrderId { get; set; }
[XmlElement("Items")]
public ItemsContainer Items;
}
public class ItemsContainer
{
[XmlAttribute("Number")]
public Int32 Number { get; set; }
[XmlElement("Item")]
public List<Item> Items;
}
public class Item
{
[XmlElement("ItemName")]
public string ItemName { get; set; }
[XmlElement("Color")]
public string Color { get; set; }
}
As you'll notice I added some attributes to let the XML parser know how to handle the class when it's converting from/to the XML. I also added another small class called "ItemsContainer" just to hold the details on the Items tag. If you didn't need the "Number" attribute, then you could probably find a way to do away with this. However, this should get you in the right direction.
The example I provided is a simple version of how I usually handle the situation, obviously there's some improvements you can make depending on your needs.
Edit
I changed the Item class to use ItemName instead of TransactionId. It was an oversight on my part.
Edit 2
Here's the corrections you need to make to the newly posted code. The reason the Order class worked in the previous example was it matched the root XML element. You're new XML does align with the base class. So we need to add in a couple more attributes to make this work. You can also remove your ItemReply class. It's not needed.
So here's the new classes:
[XmlRoot("ITEM_REPLY")]
public class ItemAvlReply
{
[XmlElement("TRAN_ID")]
public string TRAN_ID { get; set; }
[XmlElement("REPLY_CODE")]
public string REPLY_CODE { get; set; }
[XmlElement("UNIT_PRICE")]
public string UNIT_PRICE { get; set; }
[XmlElement("SUP_LOCS")]
public SupplierLocations SUP_LOCS;
[XmlElement("MESSAGE")]
public string MESSAGE { get; set; }
[XmlElement("QTY_BREAKS")]
public string QTY_BREAKS { get; set; }
}
public class SupplierLocations
{
[XmlElement("SUP_LOC")]
public List<SupplierLocation> SUP_LOC;
}
public class SupplierLocation
{
[XmlElement("SUP_LOC_ID")]
public string SUP_LOC_ID { get; set; }
[XmlElement("COUNTRY_ID")]
public string COUNTRY_ID { get; set; }
[XmlElement("QTY_AVL")]
public string QTY_AVL { get; set; }
[XmlElement("ITEM_UOM")]
public string ITEM_UOM { get; set; }
}
Everything else should remain the same. The parsing/converting the XML to classes should work without any changes.

how to deserialize xml to list in RestSharp?

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;

C# XML Serialization - How to Serialize attribute in Class that inherits List<Object>?

I need to create an XML file using C#.
I am using a class that inherits List that represents a list of computers and later initialize it with values but the serializer doesn't get the attributes for this class, only for its descendants.
this is the class:
public class Computers : List<Computer>
{
[XmlAttribute("StorageType")]
public int StorageType { get; set; }
[XmlAttribute("StorageName")]
public string StorageName { get; set; }
}
public class Computer
{
[XmlAttribute("StorageType")]
public int StorageType { get; set; }
[XmlAttribute("StorageName")]
public string StorageName { get; set; }
public string IPAddress { get; set; }
public string Name { get; set; }
}
The result should look something like this:
<fpc4:Computers StorageName="Computers" StorageType="1">
<fpc4:Computer StorageName="{D37291CA-D1A7-4F34-87E4-8D84F1397BEA}" StorageType="1">
<fpc4:IPAddress dt:dt="string">127.0.0.1</fpc4:IPAddress>
<fpc4:Name dt:dt="string">Computer1</fpc4:Name>
</fpc4:Computer>
<fpc4:Computer StorageName="{AFE5707C-EA71-4442-9CA8-2A6264EAA814}" StorageType="1">
<fpc4:IPAddress dt:dt="string">127.0.0.1</fpc4:IPAddress>
<fpc4:Name dt:dt="string">Computer2</fpc4:Name>
</fpc4:Computer>
But what I get so far is this:
<fpc4:Computers>
<fpc4:Computer StorageType="1" StorageName="{7297fc09-3142-4284-b2e9-d6ea2fb1be78}">
<fpc4:IPAddress>127.0.0.1</fpc4:IPAddress>
<fpc4:Name>Computer1</fpc4:Name>
</fpc4:Computer>
<fpc4:Computer StorageType="1" StorageName="{eab517f6-aca9-4d01-a58b-143f2e3211e7}">
<fpc4:IPAddress>127.0.0.1</fpc4:IPAddress>
<fpc4:Name>Computer2</fpc4:Name>
</fpc4:Computer>
</fpc4:Computers>
As you can see the Computers node which is the parent node doesn't get the attributes.
Do you guys have a solution?
XmlSerializer treats lists completely separate to leaf nodes; properties on lists do not exist - it is just a collection of the contained data. A better approach would be:
public class Computers {
private readonly List<Computer> items = new List<Computer>();
[XmlElement("Computer")]
public List<Computer> Items { get { return items; } }
[XmlAttribute("StorageType")]
public int StorageType { get; set; }
[XmlAttribute("StorageName")]
public string StorageName { get; set; }
}
This is an object that has a set of computers and has two attributes - but is not a list itself. The use of XmlElementAttribute for the list flattens the nesting as desired. Note that I have omitted namespaces for convenience.
Inheriting from a list (with an aim of adding members) will not work well, not just for XmlSerlaizer, but for a wide range of serializers and binding frameworks.

Categories

Resources