Deserialization xml on Multiple Child c# - c#

I have a list of Invoice and I have to deserializate it. First I serialized to xml and it ways easy to find and apply. This time I can not deserializate the Item lists in the xml. For example, this is my xml serialized row:
<?xml version="1.0" encoding="utf-8"?>
<Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Invoice>4711</Invoice>
<Table>8</Table>
<Outlet>3</Outlet>
<User>17</User>
<Creation>20140101</Creation>
<Item>
<Type>Revenue</Type>
<Productgroup>5</Productgroup>
<TotalAmount>6</TotalAmount>
<TaxClassification>1</TaxClassification>
<Text>Pizza Tonna</Text>
</Item>
<Item>
<Type>Gratuity</Type>
<TotalAmount>1.5</TotalAmount>
<Text>Tip</Text>
</Item>
<Item>
<Type>Payment</Type>
<TotalAmount>7.5</TotalAmount>
<ResNo>4812</ResNo>
</Item>
</Body>
As you see, I have a Body root xml and 5 mainly row but in the deep I have more than one Item data which is a list. If I had one item for each Invoice, it would be easy to do but during tough researches, I could not get any point.
Let me sahre with you what I did. These are my Body and Item Classes:
[XmlRoot("Body")]
public class Body
{
[XmlElement("Invoice")]
public int Invoice { get; set; }
[XmlElement("Table")]
public int Table { get; set; }
[XmlElement("Outlet")]
public int Outlet { get; set; }
[XmlElement("User")]
public int User { get; set; }
[XmlElement("Creation")]
public int Creation { get; set; }
[XmlElement("Item")]
public List<Item> Items { get; set; }
}
[XmlRoot("Item")]
public class Item
{
[XmlElement("Type")]
public string? Type { get; set; }
[XmlElement("Productgroup")]
public int? Productgroup { get; set; }
public bool ShouldSerializeProductgroup()
{
return Productgroup.HasValue;
}
[XmlElement("TotalAmount")]
public double? TotalAmount { get; set; }
public bool ShouldSerializeTotalAmount()
{
return TotalAmount.HasValue;
}
[XmlElement("TaxClassification")]
public double? TaxClassification { get; set; }
public bool ShouldSerializeTaxClassification()
{
return TaxClassification.HasValue;
}
[XmlElement("Text")]
public string? Text { get; set; }
[XmlElement("ResNo")]
public int? ResNo { get; set; }
public bool ShouldSerializeResNo()
{
return ResNo.HasValue;
}
}
And here is my main program for serialization and nice versa.
static void Main(string[] args)
{
try
{
Body body = new Body
{
Invoice = 4711,
Table = 8 / 1,
Outlet = 3,
User = 17,
Creation = 20140101,
Items = new List<Item>()
{
new Item{Type="Revenue",Productgroup = 5,TotalAmount=6.00,TaxClassification=1,Text="Pizza Tonna"},
new Item{Type="Gratuity",TotalAmount=1.50,Text="Tip"},
new Item{Type="Payment",TotalAmount=7.50,ResNo=4812}
}
};
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Body));
StreamWriter sw = new StreamWriter("CloseInvoice.xml");
xmlSerializer.Serialize(sw, body);
sw.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
try
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Body));
StreamReader sr = new StreamReader("CloseInvoice.xml");
Body body = (Body)xmlSerializer.Deserialize(sr);
Console.WriteLine("Invoice Information");
Console.WriteLine("Type: "+body.Invoice);
Console.WriteLine("Table: " + body.Table);
Console.WriteLine("Outlet: " + body.Outlet);
Console.WriteLine("User: " + body.User);
Console.WriteLine("Creation: " + body.Creation);
}
catch (Exception)
{
throw;
}
}
I tried all loops but I cannot write the Items on the console. Can you please help me? Thank from now.
Edit: Here is my console out. I can get Body Rows but I want to get Item List in the xml too.

What is your problem ? It's working for me:
for (int i = 0; i < body.Items.Count; i++)
{
var item = body.Items[i];
Console.WriteLine("ProductGroup: " + item.Productgroup);
Console.WriteLine("ResNo: " + item.ResNo);
Console.WriteLine("TaxClassification: " + item.TaxClassification);
Console.WriteLine("Text: " + item.Text);
Console.WriteLine("TotalAmount: " + item.TotalAmount);
Console.WriteLine("Type: " + item.Type);
}

Well, It seems you're interested in just looping through the Body.Items collection here. So, I think #Pierre Michel's covers that.
However, given your comments, I want to extend on that answer by including the use of Reflection with PropertyInfo of the type Item.
My answer will showcase conditionally checking for the existence of a value being specified. (i.e. when not null as a reference type, and not the default value when a value type.
For each PropertyInfo in each instance of Item, we'll do a conditional check on whether that Property is null or default, and we'll perform a Console.Write when a value is determined to exist and be specified (and not default).
Note we'll include using System.Reflection; in the file.
foreach (var item in body.Items)
{
WriteItemPropertiesThatHaveValues(item);
}
void WriteItemPropertiesThatHaveValues<T>(T item)
{
foreach (PropertyInfo pi in typeof(Item).GetProperties())
{
WriteItemPropertyThatHasValue(item, pi);
}
Console.WriteLine();
}
void WriteItemPropertyThatHasValue<T>(T item, PropertyInfo pi)
{
T defaultTValue = default;
var itemValue = pi.GetValue(item);
if (!(itemValue?.Equals(defaultTValue) ?? true))
Console.WriteLine($"\t{pi.Name} => { itemValue }");
}

Related

Why am I getting the last element of the list?

Here is the GET REQUEST
var destname = textBox1.Text;
var client1 = new RestClient("https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/autosuggest/v1.0/UK/GBP/en-GB/?query=" + destname);
var request1 = new RestRequest(Method.GET);
request1.AddHeader("x-rapidapi-key", "");
request1.AddHeader("x-rapidapi-host", "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com");
IRestResponse response1 = client1.Execute(request1);
var results = JsonConvert.DeserializeObject<DestinationName>(response1.Content);
And here is the classes
public partial class Place1
{
public string PlaceId { get; set; }
public string PlaceName { get; set; }
public string CountryId { get; set; }
public string RegionId { get; set; }
public string CityId { get; set; }
public string CountryName { get; set; }
}
public partial class DestinationName
{
public List<Place1> Places { get; set; }
}
When I do what is below I should be getting ES-sky which is the first element of the list but for some reason it gives me the last element of the list.
foreach (var a in results.Places)
{
label1.Text = a.PlaceId;
}
Here is the list
ES-sky
BCN-sky
ALC-sky
AGP-sky
MAD-sky
PMI-sky
IBZ-sky
TENE-sky
TFS-sky
TFN-sky
How would I adapt the code so that my output is ES-sky and not TFN-sky.
You are looping through the list, every time its writing value to "label1.Text". Use SingleOrDefault()/FirstOrDefault(). Dont use foreach loop.
Example:
var firstValue=results.Places.FirstOrDefault();
label1.Text = firstValue.PlaceId;
Instead of iterating through the loop here:
foreach (var a in results.Places)
{
label1.Text = a.PlaceId;
}
Just get the first value with appropriate validation:
if(results.Places != null && results.Places.Any())
{
var result = results.Places.First();
label1.Text = a.PlaceId;
}
You can bind only first place data in list from server side and get it on client side OR
bind FirstorDefault method to get first place data from list in client side.

How to solve: "You need to add XmlChoiceIdentifierAttribute to the member." when using XmlAttributeOverrides?

I'm trying to use XmlAttributeOverrides to change the way my class is being serialized to XML. I need to exclude some properties and include others in a specific order.
I have this code here:
// XML Attribute Overrrides
public static XmlAttributeOverrides GetXMLAttributeOverrides(Type theType, List<string> propertiesToInlcudeInOrder, List<string> allColumnNames)
{
try
{
if (propertiesToInlcudeInOrder != null)
{
XmlAttributeOverrides theXMLAttributeOverrides = new XmlAttributeOverrides();
if (propertiesToInlcudeInOrder.Count > 0)
{
XmlAttributes mainNewXMLAttributes = new XmlAttributes();
mainNewXMLAttributes.XmlIgnore = false;
XmlAttributes ignoreXMLAttributes = new XmlAttributes();
ignoreXMLAttributes.XmlIgnore = true;
List<string> propertiesToNotInclude = new List<string>();
foreach (string theColumnName in allColumnNames)
{
string thePropertyName = theColumnName;
bool addProperty = true;
foreach (string propertyToInclude in propertiesToInlcudeInOrder)
{
if (thePropertyName == propertyToInclude)
{
addProperty = false;
break;
}
}
if (addProperty)
{
propertiesToNotInclude.Add(thePropertyName);
}
}
// To Ignore
foreach (string propertyNameToNotInlcude in propertiesToNotInclude)
{
XmlElementAttribute theXMLElementAttributeToAdd = new XmlElementAttribute(propertyNameToNotInlcude);
theXMLElementAttributeToAdd.ElementName = propertyNameToNotInlcude;
ignoreXMLAttributes.XmlElements.Add(theXMLElementAttributeToAdd);
theXMLAttributeOverrides.Add(theType, propertyNameToNotInlcude, ignoreXMLAttributes);
}
// To Add In Order
int counter = 1;
foreach (string propertyNameToIncludeInOrder in propertiesToInlcudeInOrder)
{
XmlElementAttribute theXMLElementAttributeToAdd = new XmlElementAttribute(propertyNameToIncludeInOrder);
theXMLElementAttributeToAdd.ElementName = propertyNameToIncludeInOrder;
theXMLElementAttributeToAdd.Order = counter;
mainNewXMLAttributes.XmlElements.Add(theXMLElementAttributeToAdd);
theXMLAttributeOverrides.Add(theType, propertyNameToIncludeInOrder, mainNewXMLAttributes);
counter++;
}
}
return theXMLAttributeOverrides;
}
else
{
return null;
}
}
catch (Exception ex)
{
MessageBox.Show("Error at 'GetXMLAttributeOverrides'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return null;
}
}
In my test I have a class with 13 properties and I want to include 3 in a specific order and exclude all the others.
I have made sure that I don't have any duplicates in my lists. I have double checked that I don't have the same property name in the 'Ignore List' and the 'Include List'. I get an exception on this line of my code: XmlSerializer(dataToSerialize.GetType(), allXMLAttribueOverrides);
allXMLAttribueOverrides Is returned from my method GetXMLAttributeOverrides
The exception is:
There was an error reflecting type
'System.Collections.Generic.List`1[SystemName.UserControls.TestMain]'.
Inner exception is:
There was an error reflecting property 'TextColumn'.
You need to add XmlChoiceIdentifierAttribute to the 'TextColumn'
member.
'TextColumn' is the first property in my test class
Here's my test code:
TestMain testItem = new TestMain(null, "TextColumnTEST", 5, Convert.ToDecimal(0.333), Convert.ToDecimal(0.777), DateTime.Now, "12:00:00", DateTime.Now, true, "Password", "#FFFFFF", null, null, null);
List<TestMain> dataToSerialize = new List<TestMain>();
dataToSerialize.Add(testItem);
List<string> propertiesToInlcudeInOrder = new List<string>();
propertiesToInlcudeInOrder.Add("CurrencyColumn");
propertiesToInlcudeInOrder.Add("NumberColumn");
propertiesToInlcudeInOrder.Add("TextColumn");
List<string> allColumnNames = new List<string>();
allColumnNames.Add("ID");
allColumnNames.Add("Select");
allColumnNames.Add("TextColumn");
allColumnNames.Add("NumberColumn");
allColumnNames.Add("CurrencyColumn");
allColumnNames.Add("DecimalColumn");
allColumnNames.Add("DateColumn");
allColumnNames.Add("TimeColumn");
allColumnNames.Add("DateAndTimeColumn");
allColumnNames.Add("YesNoColumn");
allColumnNames.Add("PasswordColumn");
allColumnNames.Add("ColorColumn");
allColumnNames.Add("ImageColumn");
allColumnNames.Add("DocumentColumn");
allColumnNames.Add("OtherColumn");
XmlAttributeOverrides allXMLAttribueOverrides = ReportingManipulation.GetXMLAttributeOverrides(dataToSerialize[0].GetType(), propertiesToInlcudeInOrder, allColumnNames);
using (StringWriter mainStringWriter = new StringWriter())
{
XmlSerializer mainXMLSerializer = new XmlSerializer(dataToSerialize.GetType(), allXMLAttribueOverrides);
mainXMLSerializer.Serialize(mainStringWriter, dataToSerialize);
return mainStringWriter.ToString();
}
and here's my test class:
public class TestMain
{
#region Properties
// Properties
[XmlIgnore]
public int? ID { get; set; }
[XmlIgnore]
public bool Select { get; set; }
public string TextColumn { get; set; }
public int NumberColumn { get; set; }
public decimal CurrencyColumn { get; set; }
public decimal DecimalColumn { get; set; }
public DateTime DateColumn { get; set; }
public string TimeColumn { get; set; }
public DateTime DateAndTimeColumn { get; set; }
public bool YesNoColumn { get; set; }
public string PasswordColumn { get; set; }
public string ColorColumn { get; set; }
public byte[] ImageColumn { get; set; }
public byte[] DocumentColumn { get; set; }
public byte[] OtherColumn { get; set; }
#endregion
#region Constructors
// Constructors
public TestMain()
{
try
{
}
catch (Exception ex)
{
MessageBox.Show("Error at Constructor: 'TestMain'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
public TestMain(int? theID, string theTextColumn, int theNumberColumn, decimal theCurrencyColumn, decimal theDecimalColumn, DateTime theDateColumn, string theTimeColumn, DateTime theDateAndTimeColumn, bool theYesNoColumn, string thePasswordColumn, string theColorColumn, byte[] theImageColumn, byte[] theDocumentColumn, byte[] theOtherColumn)
{
try
{
this.ID = theID;
this.TextColumn = theTextColumn;
this.NumberColumn = theNumberColumn;
this.CurrencyColumn = theCurrencyColumn;
this.DecimalColumn = theDecimalColumn;
this.DateColumn = theDateColumn;
this.TimeColumn = theTimeColumn;
this.DateAndTimeColumn = theDateAndTimeColumn;
this.YesNoColumn = theYesNoColumn;
this.PasswordColumn = thePasswordColumn;
this.ColorColumn = theColorColumn;
this.ImageColumn = theImageColumn;
this.DocumentColumn = theDocumentColumn;
this.OtherColumn = theOtherColumn;
}
catch (Exception ex)
{
MessageBox.Show("Error at Constructor: 'TestMain'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
#endregion
}
Where am I going wrong?
Any help / advice would be appreciated.
Your basic problem is that you are adding multiple override [XmlElement] attributes for each of your properties, because you are using a single instance mainNewXMLAttributes for all of them, which accumulates the XmlElementAttribute objects defined for all of them.
To fix this, you need to allocate a fresh mainNewXMLAttributes for each property inside the foreach (var propertyNameToIncludeInOrder in propertiesToInlcudeInOrder) loop, as shown in the following corrected and simplified version of GetXMLAttributeOverrides():
public static partial class ReportingManipulation
{
public static XmlAttributeOverrides GetXMLAttributeOverrides(Type theType, IList<string> propertiesToInlcudeInOrder)
{
var allProperties = theType.GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance).Select(p => p.Name);
return GetXMLAttributeOverrides(theType, propertiesToInlcudeInOrder, allProperties);
}
// XML Attribute Overrrides
public static XmlAttributeOverrides GetXMLAttributeOverrides(Type theType, IList<string> propertiesToInlcudeInOrder, IEnumerable<string> allProperties)
{
if (propertiesToInlcudeInOrder == null || propertiesToInlcudeInOrder.Count == 0)
return null;
var theXMLAttributeOverrides = new XmlAttributeOverrides();
// To Add In Order
int counter = 1;
foreach (var propertyNameToIncludeInOrder in propertiesToInlcudeInOrder)
{
// Allocate a fresh instance of XmlAttributes for each property, because we are defining a different
// XmlElementAttribute for each
var mainNewXMLAttributes = new XmlAttributes { XmlIgnore = false };
// Specify the element order XmlElementAttribute and attach to the XmlAttributes
var theXMLElementAttributeToAdd = new XmlElementAttribute { Order = counter };
mainNewXMLAttributes.XmlElements.Add(theXMLElementAttributeToAdd);
// Attach the override XmlElementAttribute to the property propertyNameToIncludeInOrder
theXMLAttributeOverrides.Add(theType, propertyNameToIncludeInOrder, mainNewXMLAttributes);
counter++;
}
// To Ignore
// Using System.Linq.Enumerable.Except()
var propertiesToNotInclude = allProperties.Except(propertiesToInlcudeInOrder);
var ignoreXMLAttributes = new XmlAttributes { XmlIgnore = true };
foreach (var propertyNameToNotInlcude in propertiesToNotInclude)
{
// Attach the override XmlElementAttribute to the property propertyNameToIncludeInOrder
// No need to allocate a fresh instance of ignoreXMLAttributes for each, because the instances would all be identical
theXMLAttributeOverrides.Add(theType, propertyNameToNotInlcude, ignoreXMLAttributes);
}
return theXMLAttributeOverrides;
}
}
Why does your code not work? In your initial code, you do:
XmlAttributes mainNewXMLAttributes = new XmlAttributes();
mainNewXMLAttributes.XmlIgnore = false;
int counter = 1;
foreach (string propertyNameToIncludeInOrder in propertiesToInlcudeInOrder)
{
XmlElementAttribute theXMLElementAttributeToAdd = new XmlElementAttribute(propertyNameToIncludeInOrder);
theXMLElementAttributeToAdd.ElementName = propertyNameToIncludeInOrder;
theXMLElementAttributeToAdd.Order = counter;
mainNewXMLAttributes.XmlElements.Add(theXMLElementAttributeToAdd);
theXMLAttributeOverrides.Add(theType, propertyNameToIncludeInOrder, mainNewXMLAttributes);
counter++;
}
Now, the method XmlAttributeOverrides.Add(Type, String, XmlAttributes) is documented to work as follows:
Adds an XmlAttributes object to the collection of XmlAttributes objects. The type parameter specifies an object to be overridden. The member parameter specifies the name of a member that is overridden.
Thus the contents of mainNewXMLAttributes will get applied to the named parameter when the XmlSerializer is eventually constructed. And as you construct only instance of mainNewXMLAttributes for all parameters, its XmlElements array will contain element names corresponding to all of the parameters! I.e. you code attempts to apply multiple [XmlElement] attributes to each named parameter, differing only in override name and order. This accounts for the You need to add XmlChoiceIdentifierAttribute to the 'TextColumn' member. exception -- you can only attach multiple element names to a property if the property value is polymorphic and you want to assign different element names to different value types.
Notes
When generating an XmlSerializer with overrides, you must cache it statically and reuse it later to avoid a severe memory leak, as explained in Memory Leak using StreamReader and XmlSerializer.
I don't recommend unconditionally swallowing exceptions and presenting them as error messages to the user in low-level utility methods or object constructors.
Demo working fiddle here.

Nested JSON Array C#

I am trying to deserialize a JSON array that has an additional nested object.
Here is a sample C# code. It returns data until it gets to the second array. I know it needs a second foreach loop but I can't seem to get it to work. Any help would be greatly appreciated. Thank you.
string sJSON = #" [{""dateNumeric"":1216000000,""hourOfDay"":0,""customerNumber"":12,""storedepartment"":[{""department"":333,""descriptionOfDepartment"":""Department A""},{""department"":111,""descriptionOfDepartment"":""Department B""}]},{""dateNumeric"":1216000000,""hourOfDay"":3,""customerNumber"":3,""storedepartment"":[{""department"":999,""descriptionOfDepartment"":""Department X""},{""department"":888,""descriptionOfDepartment"":""Department Y""}]}]";
JArray a = JArray.Parse(sJSON);
foreach (JObject o in a.Children<JObject>())
{
foreach (JProperty p in o.Properties())
{
string name = p.Name;
string value = (string)p.Value;
Console.WriteLine(name + "-- " + value);
}
}
You can use Newtonsoft.json library for serializing/deserializing the data, as per your requirement kindly follow below code :
First you need to create the class which will capture the data in specified format(as per your data requirement) :
public class RootObject
{
public int dateNumeric { get; set; }
public int hourOfDay { get; set; }
public int customerNumber { get; set; }
public List<Storedepartment> storedepartment { get; set; }
}
public class Storedepartment
{
public int department { get; set; }
public string descriptionOfDepartment { get; set; }
}
Now , for deserialising the Json data use below statement :
string sJSON = #" [{""dateNumeric"":1216000000,""hourOfDay"":0,""customerNumber"":12,""storedepartment"":[{""department"":333,""descriptionOfDepartment"":""Department A""},{""department"":111,""descriptionOfDepartment"":""Department B""}]},{""dateNumeric"":1216000000,""hourOfDay"":3,""customerNumber"":3,""storedepartment"":[{""department"":999,""descriptionOfDepartment"":""Department X""},{""department"":888,""descriptionOfDepartment"":""Department Y""}]}]";
List<RootObject> Data = JsonConvert.DeserializeObject<List<RootObject>>(sJSON);
Once you get data in your list you can perform your required operation on list
Actually you might be able to properly parse your JSON string if you use the following approach.
public static void ProcessJObject(JProperty obj)
{
string name = obj.Name;
string value = "";
if (obj.Value.Type == JTokenType.Array)
{
Console.WriteLine("Array: " + name);
ProcessJArray((JArray)obj.Value);
}
else
{
value = (string)obj.Value;
Console.WriteLine(name + "-- " + value);
}
}
public static void ProcessJArray(JArray arr)
{
foreach (JObject o in arr.Children<JObject>())
{
foreach (JProperty p in o.Properties())
{
ProcessJObject(p);
}
}
}
static void Main(string[] args)
{
string sJSON = #" [{""dateNumeric"":1216000000,""hourOfDay"":0,""customerNumber"":12,""storedepartment"":[{""department"":333,""descriptionOfDepartment"":""Department A""},{""department"":111,""descriptionOfDepartment"":""Department B""}]},{""dateNumeric"":1216000000,""hourOfDay"":3,""customerNumber"":3,""storedepartment"":[{""department"":999,""descriptionOfDepartment"":""Department X""},{""department"":888,""descriptionOfDepartment"":""Department Y""}]}]";
JArray a = JArray.Parse(sJSON);
ProcessJArray(a);
Console.Read();
}
So what have I done here. I simply divided the problem of parsing json in two smaller problems one that is solved by the ProcessJObject function (parsing and printing JProperty) and one that is solved by the ProcessJArray function (looping through the JArray and for each JProperty it contain passing it to ProcessJObject function). So now the parsing problem upto any point of nesting of JSON array is solved by the above approach.
Hope it helps.
static void JsonConvert()
{
string sJSON = #"[{""dateNumeric"":1216000000,""hourOfDay"":0,""customerNumber"":12,""storedepartment"":[{""department"":333,""descriptionOfDepartment"":""Department A""},{""department"":111,""descriptionOfDepartment"":""Department B""}]},{""dateNumeric"":1216000000,""hourOfDay"":3,""customerNumber"":3,""storedepartment"":[{""department"":999,""descriptionOfDepartment"":""Department X""},{""department"":888,""descriptionOfDepartment"":""Department Y""}]}]";
var storeDetail = Newtonsoft.Json.JsonConvert.DeserializeObject<List<StoreDetail>>(sJSON);
//iterate your list here
}
public class StoreDetail
{
[JsonProperty("dateNumeric")]
public string DateNumeric { get; set; }
[JsonProperty("hourOfDay")]
public int HourOfDay { get; set; }
[JsonProperty("customerNumber")]
public int CustomerNumber { get; set; }
[JsonProperty("storedepartment")]
public List<StoreDepartment> StoreDepartment { get; set; }
}
public class StoreDepartment
{
[JsonProperty("department")]
public int Department { get; set; }
[JsonProperty("descriptionOfDepartment")]
public string DescriptionOfDepartment { get; set; }
}
string sJSON = #" [{""dateNumeric"":1216000000,""hourOfDay"":0,""customerNumber"":12,""storedepartment"":[{""department"":333,""descriptionOfDepartment"":""Department A""},{""department"":111,""descriptionOfDepartment"":""Department B""}]},{""dateNumeric"":1216000000,""hourOfDay"":3,""customerNumber"":3,""storedepartment"":[{""department"":999,""descriptionOfDepartment"":""Department X""},{""department"":888,""descriptionOfDepartment"":""Department Y""}]}]";
object dJson = Newtonsoft.Json.JsonConvert.DeserializeObject<JArray>(sJSON);
Console.WriteLine(dJson.ToString());
JArray a = JArray.Parse(sJSON);
foreach (JObject o in a.Children<JObject>())
{
foreach (JProperty p in o.Properties())
{
if (p.First.Count() == 0)
{
string name = p.Name;
string value = (string)p.Value;
Console.WriteLine(name + "-- " + value);
}
else
{
string name = p.Name;
Console.WriteLine(name);
string subStr = p.Value.ToString();
JArray aSub = JArray.Parse(subStr);
foreach (JObject oSub in aSub.Children<JObject>())
{
foreach (JProperty pSub in oSub.Properties())
{
string nameSub = pSub.Name;
string valueSub = (string)pSub.Value;
Console.WriteLine("\t" + nameSub + "-- " + valueSub);
}
}
}
}
}
Console.ReadLine();

Deserialising XML with different element name in c#

XML:
<?xml version="1.0" encoding="UTF-8"?>
<Images>
<I0>
<Path>123.com</Path>
<I0>
<I1>
<Path>123.com</Path>
<I1>
<I2>
<Path>123.com</Path>
<I2>
</Images>
Can serializer.Deserialize() be used to get tags with different names into a collection?
currently, in my object I have:
C#:
public class rootObject
{
[XmlElement(ElementName = "I0")]
public I0 I0 { get; set; }
[XmlElement(ElementName = "I1")]
public I1 I1 { get; set; }
[XmlElement(ElementName = "I2")]
public I2 I2 { get; set; }
}
But I would like to have (Because Images can have more or fewer elements):
public class rootObject
{
public List<I> Is { get; set; }
}
You can do what you are suggesting you just merely need to pass in the type argument in your class doing the generic. The key point to remember when you do a deserialization routine is that the routine needs to know the sub reference. So if I was to say string.Deserialize it would bomb. It would need to know a reference string.Deserialize> where Sub could be the class object that may change.
Say I have a base class and I want 'T' to be a type I can change for extensible abilities later.
[Serializable]
public class Test<T> where T : class
{
public Test() { }
public int TestId { get; set; }
public string Name { get; set; }
public List<T> Shipments { get; set; }
}
I want to test this with two classes I just make up that have different properties slightly
[Serializable]
public class Sub1
{
public int Id { get; set; }
public string Desc { get; set; }
}
[Serializable]
public class Sub2
{
public int IdWhatever { get; set; }
public string DescWhatever { get; set; }
}
Now let's do a main program and test serialization.
class Program
{
static void Main(string[] args)
{
var serializeTest = new Test<Sub1> { TestId = 1, Name = "Test", Shipments = new List<Sub1> { new Sub1 { Id = 1, Desc = "Test" }, new Sub1 { Id = 2, Desc = "Test2" } } };
var serializeTest2 = new Test<Sub2> { TestId = 1, Name = "Test", Shipments = new List<Sub2> { new Sub2 { IdWhatever = 1, DescWhatever = "Test" }, new Sub2 { IdWhatever = 2, DescWhatever = "Test2" } } };
var serialized = serializeTest.SerializeToXml();
var serialized2 = serializeTest2.SerializeToXml();
var deserialized = serialized.DeserializeXml<Test<Sub1>>();
var deserialized2 = serialized2.DeserializeXml<Test<Sub2>>();
Console.WriteLine(serialized);
Console.WriteLine();
Console.WriteLine(serialized2);
Console.ReadLine();
}
}
And my Serialize and DeSerialize extension methods:
public static string SerializeToXml<T>(this T valueToSerialize, string namespaceUsed = null)
{
var ns = new XmlSerializerNamespaces(new XmlQualifiedName[] { new XmlQualifiedName(string.Empty, (namespaceUsed != null) ? namespaceUsed : string.Empty) });
using (var sw = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sw, new XmlWriterSettings { OmitXmlDeclaration = true }))
{
dynamic xmler = new XmlSerializer(typeof(T));
xmler.Serialize(writer, valueToSerialize, ns);
}
return sw.ToString();
}
}
public static T DeserializeXml<T>(this string xmlToDeserialize)
{
dynamic serializer = new XmlSerializer(typeof(T));
using (TextReader reader = new StringReader(xmlToDeserialize))
{
return (T)serializer.Deserialize(reader);
}
}
You don't need to specify the XmlElement name when the properties match the XML. A few solutions, some kinda hacky :).
HACKY: use regex string replace to replace <I#> and </I#> to
just <I> and </I>
SOMEWHAT HACKY: This might work for you:
How to deserialize an XML array containing multiple types of elements in C#,
but you'd have to add an attribute for i0, i1 ... i100, etc.
BEST: Is that your entire XML? I'd honestly just use LINQToXml and
do a Descendants("Path") and get an array of strings back with 1 line of code. Serialization is not really the best solution for this.

Write xml with c#?

I create a form so I use textbox and I would like when I push the button "send" he filled xml :
for exemple 1 time :
<?xml version="1.0" encoding="utf-8"?>
<DonneesLocale>
<Donnee>
<id>1</id>
<libelle>bla </libelle>
<email_asso>bla#</email_asso>
<login>bla</login>
<psw>bla</psw>
<site>bla</site>
<description>bla</description>
<data_1_lib></data_1_lib>
<data_1_val></data_1_val>
<data_2_lib></data_2_lib>
<data_2_val></data_2_val>
</Donnee>
</DonneesLocale>
and 2nd time when I push the button:
<?xml version="1.0" encoding="utf-8"?>
<DonneesLocale>
<Donnee>
<id>1</id>
<libelle>bla </libelle>
<email_asso>bla#</email_asso>
<login>bla</login>
<psw>bla</psw>
<site>bla</site>
<description>bla</description>
<data_1_lib></data_1_lib>
<data_1_val></data_1_val>
<data_2_lib></data_2_lib>
<data_2_val></data_2_val>
</Donnee>
<DonneesLocale>
<Donnee>
<id>2</id>
<libelle>hello</libelle>
<email_asso>hello#</email_asso>
<login>hello</login>
<psw>hello</psw>
<site>hello</site>
<description>hello</description>
<data_1_lib></data_1_lib>
<data_1_val></data_1_val>
<data_2_lib></data_2_lib>
<data_2_val></data_2_val>
</Donnee>
</DonneesLocale>
Someone can help me please ?
(Sorry for my English !)
Thanks !
So if I get it correct, you want to append new data to your existing xml.
For that you can chose to temporarily store the current xml and and add new data to it using Linq Xml.
To do this, now you need to modify your current code with an additional check for xml file exists before all calls to press button event. The code for appending to xml can be found Appending an existing XML file with XmlWriter
If you are using a list of objects then you can update the list on button click and parse your object list in xml like below:
var xml = new XElement("DonneesLocales", DonneesLocalList.Select(x => new XElement("DonneesLocale",
new XElement("Donnee",
new XElement("id"),
new XElement("libelle"),
new XElement("email_asso"),
new XElement("login"),
new XElement("psw"),
new XElement("site"),
new XElement("description"),
new XElement("data_1_lib"),
new XElement("data_1_val"),
new XElement("data_2_lib"),
new XElement("data_2_val")))));
Another option is their for XML Serialization and Deserialization which will be done with the help of XMLSerializer,
public class DonneesLocale
{
private List<Donnee> donnee = new List<Donnee>();
[XmlArray("DonneesLocale")]
public List<Donnee> Donnee
{
get { return donnee; }
set { donnee = value; }
}
}
[XmlType("Donnee")]
public class Donnee
{
[XmlElement("id")]
public int id { get; set; }
[XmlElement("libelle")]
public string libelle { get; set; }
[XmlElement("email_asso")]
public string email_asso { get; set; }
[XmlElement("login")]
public string login { get; set; }
[XmlElement("psw")]
public string psw { get; set; }
[XmlElement("site")]
public string site { get; set; }
[XmlElement("description")]
public string description { get; set; }
[XmlElement("data_1_lib")]
public string data_1_lib { get; set; }
[XmlElement("data_1_val")]
public string data_1_val { get; set; }
[XmlElement("data_2_lib")]
public string data_2_lib { get; set; }
[XmlElement("data_2_val")]
public string data_2_val { get; set; }
}
DonneesLocale dnl = new DonneesLocale();
private void Serialize(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
var temp = new Donnee() { id = i, libelle = "libelle " + i, email_asso = "email_asso " + i, login = "login " + i, psw = "psw " + i, site = "site " + i, description = "description " + i, data_1_lib = "data_1_lib " + i, data_1_val = "data_1_val " + i, data_2_lib = "data_2_lib " + i, data_2_val = "data_2_val " + i };
dnl.Donnee.Add(temp);
}
try
{
// to Save columnorders to the file
var serializer = new XmlSerializer(typeof(DonneesLocale));
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
using (TextWriter writer = new StreamWriter(#"Your XML Path"))
{
serializer.Serialize(writer, dnl, ns);
}
}
catch { }
}
private void Deserialize(object sender, EventArgs e)
{
try
{
if (File.Exists(#"Your XML Path"))
{
var deserializer = new XmlSerializer(typeof(DonneesLocale));
using (TextReader reader = new StreamReader(#"Your XML Path"))
{
dnl = (DonneesLocale)deserializer.Deserialize(reader);
}
}
}
catch
{
}
}
all you need is to add an object to the list and serialize the object to XML whenever you want,
this will work as you expected,

Categories

Resources