I receive an XML file from a server, which contains many elements, and one attribute.
When Try and serialize/deserialize the xml, all the elements are serialized/deserialized properly, except for the attribute. Why does this happen?
here is the XML file:
"<msg><msisdn>123456789</msisdn><sessionid>535232573</sessionid><phase>2</phase><request type=\"1\">*120*111#</request></msg>"
and the class:
[Serializable]
[XmlRoot(ElementName = "msg", Namespace = "")]
public class myClass
{
[XmlElement(ElementName = "msisdn")]
public string number = string.Empty;
[XmlElement(ElementName = "sessionid")]
public string sessionID = string.Empty;
[XmlAttribute(AttributeName = "type")]
public string requestType = string.Empty;
[XmlElement(ElementName = "request")]
public string request = string.Empty;
[XmlElement(ElementName = "phase")]
public string phase = string.Empty;
public override string ToString()
{
return number + " - " + sessionID;
}
}
Thanks for any help
Try defining a sub class for request because type is attribute of the request and not of the root MyClass:
[XmlRoot(ElementName = "msg", Namespace = "")]
public class MyClass
{
[XmlElement(ElementName = "msisdn")]
public string Number { get; set; }
[XmlElement(ElementName = "sessionid")]
public string SessionID { get; set; }
[XmlElement(ElementName = "request")]
public Request Request { get; set; }
[XmlElement(ElementName = "phase")]
public string Phase { get; set; }
public override string ToString()
{
return Number + " - " + SessionID;
}
}
public class Request
{
[XmlAttribute(AttributeName = "type")]
public string Type { get; set; }
[XmlText]
public string Value { get; set; }
}
class Program
{
static void Main()
{
var serializer = new XmlSerializer(typeof(MyClass));
using (var stringReader = new StringReader("<msg><msisdn>123456789</msisdn><sessionid>535232573</sessionid><phase>2</phase><request type=\"1\">*120*111#</request></msg>"))
using (var xmlReader = XmlReader.Create(stringReader))
{
var obj = (MyClass)serializer.Deserialize(xmlReader);
Console.WriteLine(obj.Request.Type);
}
}
}
Related
I am working with two different databases. I am passing the Model collection (SQL Server) to a ViewModel collection. The ViewModel has extra properties which I access out of a Visual Fox Pro database. I am able to map the existing properties, but the ViewModel does not save the data after passing the values to it.
The WoCust and the Lname fields return null, but the rest of the properties which come from the original Model pass to the properties in the ViewModel fine.
When I debug at the rdr for the OleDbCommand, it shows that the ViewModel is receiving a value for both rdr[WoCust] and rdr[Lname].
How do I make it so the ViewModel saves the new values?
WOSchedule.cs...
public partial class WOSchedule
{
public long Id { get; set; }
public string WoNo { get; set; }
public Nullable<long> QuoteTypeId { get; set; }
public Nullable<int> PriorityNo { get; set; }
public bool Active { get; set; }
public Nullable<System.DateTime> WoDate { get; set; }
public Nullable<long> QuoteID { get; set; }
public Nullable<System.DateTime> WoDone { get; set; }
public Nullable<long> WOScheduleListId { get; set; }
public string StorageLocation { get; set; }
public virtual QuoteType QuoteType { get; set; }
public virtual Quote Quote { get; set; }
public virtual WOScheduleList WOScheduleList { get; set; }
}
WoWcheduleVM.cs...
public partial class WoScheduleVM
{
public long Id { get; set; }
public string WoNo { get; set; }
public Nullable<long> QuoteTypeId { get; set; }
public Nullable<int> PriorityNo { get; set; }
public bool Active { get; set; }
public DateTime? WoDate { get; set; }
public Nullable<long> QuoteID { get; set; }
public DateTime? WoDone { get; set; }
public Nullable<long> WOScheduleListId { get; set; }
public string StorageLocation { get; set; }
public string WoCust { get; set; } // extra property
public string Lname { get; set; } // extra property
public virtual QuoteType QuoteType { get; set; }
public virtual Quote Quote { get; set; }
public virtual WOScheduleList WOScheduleList { get; set; }
}
WOSchedulesController.cs
string cs = ConfigurationManager.ConnectionStrings["foxproTables"].ConnectionString;
OleDbConnection cn = new OleDbConnection(cs);
var wOSchedules = db.WOSchedules.Where(w => w.WoDone == null).Include(w => w.QuoteType);
var wOSchedulesVM = wOSchedules.Select(s => new ViewModels.WoScheduleVM()
{
Id = s.Id,
WoNo = s.WoNo,
QuoteTypeId = s.QuoteTypeId,
PriorityNo = s.PriorityNo,
Active = s.Active,
WoDate = s.WoDate,
QuoteID = s.QuoteID,
WoDone = s.WoDone,
WOScheduleListId = s.WOScheduleListId,
StorageLocation = s.StorageLocation
});
cn.Open();
foreach (var sch in wOSchedulesVM)
{
string conn = #"SELECT wo_cust, lname FROM womast INNER JOIN custmast ON womast.wo_cust = custmast.cust_id WHERE wo_no = '" + sch.WoNo + "'";
OleDbCommand cmdWO = new OleDbCommand(conn, cn);
OleDbDataReader rdr = cmdWO.ExecuteReader();
while (rdr.Read())
{
sch.WoCust = ((string)rdr["wo_cust"]).Trim();
sch.Lname = ((string)rdr["lname"]).Trim();
}
}
cn.Close();
return View(wOSchedulesVM.OrderByDescending(d => d.WoDate).ToList());
The problem is you're using foreach loop for iterating wOSchedulesVM collection, which renders the source collection immutable during iteration. The older documentation version explicitly explains that behavior:
The foreach statement is used to iterate through the collection to get
the information that you want, but can not be used to add or remove
items from the source collection to avoid unpredictable side effects. If you need to add or remove items from the source collection, use a for loop.
Therefore, you should use for loop to be able to modify property values inside that collection, as shown in example below:
using (var OleDbConnection cn = new OleDbConnection(cs))
{
cn.Open();
string cmd = #"SELECT wo_cust, lname FROM womast INNER JOIN custmast ON womast.wo_cust = custmast.cust_id WHERE wo_no = #WoNo";
// not sure if it's 'Count' property or 'Count()' method, depending on collection type
for (int i = 0; i < wOSchedulesVM.Count; i++)
{
var sch = wOSchedulesVM[i];
using (OleDbCommand cmdWO = new OleDbCommand(conn, cn))
{
cmd.Parameters.AddWithValue("#WoNo", sch.WoNo)
OleDbDataReader rdr = cmdWO.ExecuteReader();
if (rdr.HasRows)
{
while (rdr.Read())
{
sch.WoCust = (!rdr.IsDbNull(0)) ? rdr.GetString(0).Trim() : string.Empty;
sch.Lname = (!rdr.IsDbNull(1)) ? rdr.GetString(1).Trim() : string.Empty;
}
}
}
}
}
Note: This example includes 3 additional aspects, i.e. parameterized query, checking row existence with HasRows property and checking against DBNull.Value with IsDbNull().
Related issue: What is the best way to modify a list in a 'foreach' loop?
http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.atom on this site, I wrote the following code to retrieve the data.
protected void Button1_Click(object sender, EventArgs e)
{
string adresal = "http://" + txtAd.Text;
WebResponse GelenCevap;
WebRequest adresistegi = HttpWebRequest.Create(adresal);
GelenCevap = adresistegi.GetResponse();
StreamReader CevapOku = new StreamReader(GelenCevap.GetResponseStream());
string KaynakKodlar = CevapOku.ReadToEnd();
XmlDocument xmlDoc = new XmlDocument(); // Create an XML document object
xmlDoc.Load("http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.atom"); // Load the XML document from the specified file
XmlNodeList depremler = xmlDoc.GetElementsByTagName("entry");
foreach (XmlNode node in depremler)
{
var a = node.ChildNodes;
foreach (XmlElement x in a)
{
ListBox1.Items.Add(x.InnerText);
}
}
}
In this way I get all the data in the ListBox.But I need to assign them to variable data line by line.How can I do? I would appreciate if you can help.
Also i need id,title, updated, georss:point,georss:elev variables.
First add an Enrty and Category class:
public class Entry
{
public string Id { get; set; }
public string Title { get; set; }
public string Updated { get; set; }
public string Summary { get; set; }
public string GPoint { get; set; }
public string GElev { get; set; }
public List<string> Categories { get; set; }
}
public class Category
{
public string Label { get; set; }
public string Term { get; set; }
}
Then use LINQ to XML
XDocument xDoc = XDocument.Load("path");
List<Entry> entries = (from x in xDoc.Descendants("entry")
select new Entry()
{
Id = (string) x.Element("id"),
Title = (string)x.Element("title"),
Updated = (string)x.Element("updated"),
Summary = (string)x.Element("summary"),
GPoint = (string)x.Element("georss:point"),
GElev = (string)x.Element("georss:elev"),
Categories = (from c in x.Elements("category")
select new Category
{
Label = (string)c.Attribute("label"),
Term = (string)c.Attribute("term")
}).ToList();
}).ToList();
I have a strongly typed view where I have a form based on a Model Contact. In the textboxes, the default values are those of the contact that I'm passing to the view in the controller.
So I have a class Contact as follows:
public class Contact
{
public int IdContact { get; set; }
public string Nom { get; set; }
public string Prenom { get; set; }
public string Email { get; set; }
public string Fonction { get; set; }
public List<FonctionContact> ListeFonctions = new List<FonctionContact>();
public string TelephoneFixe { get; set; }
public string TelephonePort { get; set; }
public Contact() { }
public Contact(int idContact, string nom, string prenom, string email, string telephoneFixe, string telephonePort)
{
this.IdContact = idContact;
this.Nom = nom;
this.Prenom = prenom;
this.Email = email;
this.TelephoneFixe = telephoneFixe;
this.TelephonePort = telephonePort;
}
public Contact(int idContact, string nom, string prenom, List<FonctionContact> listeFonctions, string email, string telephoneFixe, string telephonePort)
{
this.IdContact = idContact;
this.Nom = nom;
this.Prenom = prenom;
this.ListeFonctions = listeFonctions;
this.Email = email;
this.TelephoneFixe = telephoneFixe;
this.TelephonePort = telephonePort;
}
}
There is a list of FonctionContact. The class of FonctionContact:
public class FonctionContact
{
public int IdFonction;
public string LibelleFonction;
public FonctionContact() { }
public FonctionContact(int idFonction, string libelleFonction)
{
this.IdFonction = idFonction;
this.LibelleFonction = libelleFonction;
}
}
So I would like to display the property ListeFonctions of the Contact in a listBoxfor but it doesn't work. There is my form where I've tried to display the list :
#using (Html.BeginForm()){
<label>Nom:</label>
#Html.TextBoxFor(contact => contact.Nom, new { #Value = #Model.Nom })
<label>Prénom:</label>
#Html.TextBoxFor(contact => contact.Prenom, new { #Value = #Model.Prenom })
///the next controls for the next properties of class Contact...
<label>Fonction(s):</label>
#Html.ListBoxFor(contact => contact.ListeFonctions, new SelectList(Model.ListeFonctions, "IdFonction", "LibelleFonction"));
}
It shows me an error: "Model.FonctionContact doesn't have a property IdFonction. So I'm stuck here, I can't find out what's wrong. Somebody has an idea ?
Basically, you'll need to provide the ListBoxFor with a list of value items (integers, which you could use to load some previously selected and saved items). Also, it will need a MultiSelectList as the second parameter (for the previously explained reason, because it's not a DropDownList with just one selected item), which would probably be neater to compose in the model, as I have written below:
Model
public class Contact
{
public int IdContact { get; set; }
public string Nom { get; set; }
public string Prenom { get; set; }
public string Email { get; set; }
public string Fonction { get; set; }
// you could save a selection of items from that list
private List<int> _selectedFonctionIds;
public List<int> SelectedFonctionIds{
get {
return _selectedFonctionIds?? new List<int>();
}
set {
_selectedFonctionIds= value;
}
}
private List<FonctionContact> _listeFonctions;
public MultiSelectList ListeFonctions{
get {
return new MultiSelectList(
_listeFonctions,
"IdFonction", // dataValueField
"LibelleFonction" // dataTextField
);
}
}
public string TelephoneFixe { get; set; }
public string TelephonePort { get; set; }
public Contact() { }
public Contact(int idContact, string nom, string prenom, string email, string telephoneFixe, string telephonePort)
{
this.IdContact = idContact;
this.Nom = nom;
this.Prenom = prenom;
this.Email = email;
this.TelephoneFixe = telephoneFixe;
this.TelephonePort = telephonePort;
}
public Contact(int idContact, string nom, string prenom, List<int> selectedFonctionIds, List<FonctionContact> listeFonctions, string email, string telephoneFixe, string telephonePort)
{
this.IdContact = idContact;
this.Nom = nom;
this.Prenom = prenom;
this.SelectedFonctionIds = selectedFonctionIds;
this._listeFonctions = listeFonctions;
this.Email = email;
this.TelephoneFixe = telephoneFixe;
this.TelephonePort = telephonePort;
}
}
inside the view's form
#Html.ListBoxFor(contact => contact.SelectedFonctionIds, Model.ListeFonctions)
Hello. I have a list that looks like this one:
public class PagedList<T> : List<T>
{
public PagedList(IEnumerable<T> collection) : base(collection)
{ }
public int TotalItems { get; set; }
public int CurrentPage { get; set; }
public int PageSize { get; set; }
//some other properties
}
and used in repository for paging
public PagedList<TEntity> GetPaged(int page)
{
var pagedEntities = some_query;
return pagedEntities.AsPagedList(totalResults, page, pageSize);
}
The same PagedList is also used in asp mvc view models for paging.
Is it possible to map this collections using Automapper with all the properties TotalItems/CurrentPage/... ?
PagedList<DbItem> dbItems = _repository.GetPages(page);
var viewItems = new PagedList<SomeItemView>();
Mapper.Map(dbItems , viewItems);
Tahnk You !
This worked for me. Are you looking for something more generic?
public class DbItem
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ViewItem
{
public int Id { get; set; }
public string Name { get; set; }
}
public class PagedList<T>: List<T>
{
public int TotalItems { get; set; }
public int CurrentPage { get; set; }
public int PageSize { get; set; }
}
class Program
{
static void Main(string[] args)
{
MapItems();
}
public static void MapItems()
{
Mapper.CreateMap<DbItem, ViewItem>();
Mapper.CreateMap<PagedList<DbItem>, PagedList<ViewItem>>()
.AfterMap((s, d) => Mapper.Map<List<DbItem>, List<ViewItem>>(s, d));
var dbList = new PagedList<DbItem>
{
new DbItem {Id = 1, Name = "a"},
new DbItem {Id = 2, Name = "b"}
};
dbList.TotalItems = 2;
dbList.CurrentPage = 1;
dbList.PageSize = 10;
var viewList = Mapper.Map<PagedList<DbItem>, PagedList<ViewItem>>(dbList);
Console.WriteLine(viewList.TotalItems);
Console.WriteLine(viewList.CurrentPage);
Console.WriteLine(viewList.PageSize);
Console.WriteLine(viewList[0].Id + " " + viewList[0].Name);
Console.WriteLine(viewList[1].Id + " " + viewList[1].Name);
Console.ReadLine();
}
}
What you need is a custom type converter
public class PagedListConverter<TIn, TOut> : ITypeConverter<IPagedList<TIn>, IPagedList<TOut>>
{
public IPagedList<TOut> Convert(AutoMapper.ResolutionContext context)
{
var source = (IPagedList<TIn>)context.SourceValue;
var mapped = Mapper.Map<IList<TOut>>(source);
return new StaticPagedList<TOut>(mapped, source.GetMetaData());
}
}
Usage:
Mapper.CreateMap<IPagedList<Company>, IPagedList<CompanyViewModel>>().ConvertUsing<PagedListConverter<Company, CompanyViewModel>>();
For those who have faced the similar problem recently, and as an update to NoPyGod's answer, you can achieve the general mapping using ITypeConverter. According to the official documentation:
AutoMapper also supports open generic type converters with any number of generic arguments:
var configuration = new MapperConfiguration(cfg =>
cfg.CreateMap(typeof(Source<>), typeof(Destination<>)).ConvertUsing(typeof(Converter<,>)));
The closed type from Source will be the first generic argument, and the closed type of Destination will be the second argument to close Converter<,>.
So the custom type converter would be:
private class Converter<TSource, TDestination>
: ITypeConverter<PagedList<TSource>, PagedList<TDestination>>
{
public PagedList<TDestination> Convert(
PagedList<TSource> source,
PagedList<TDestination> destination,
ResolutionContext context) =>
new PagedList<TDestination>(
context.Mapper.Map<List<TSource>, List<TDestination>>(source));
/* Additional settings comes here. */
}
And then register it:
this.CreateMap(typeof(PagedList<>), typeof(PagedList<>)).ConvertUsing(typeof(Converter<,>));
I have this code:
[Serializable]
[XmlRoot("ISO_CCY_CODES")]
public class IsoCurrencyCodes
{
public IsoCurrencyCodes()
{
IsoCodes = new List<IsoCurrencyCode>();
}
public IsoCurrencyCodes(List<IsoCurrencyCode> isoCodes)
{
IsoCodes = isoCodes;
}
[XmlArrayItem("ISO_CURRENCY")]
public List<IsoCurrencyCode> IsoCodes { get; set; }
public static IEnumerable<IsoCurrencyCode> Get()
{
var doc = XDocument.Parse(XmlStringIsoCodes.XmlSource.Replace("\r\n", ""));
var res = doc.Deserialize<IsoCurrencyCodes>();
return res.IsoCodes;
}
}
[Serializable]
[XmlRoot("ISO_CURRENCY")]
public class IsoCurrencyCode
{
public IsoCurrencyCode()
{
}
[XmlElement(ElementName = "ENTITY")]
public string Entity { get; set; }
[XmlElement(ElementName = "CURRENCY")]
public string Currency { get; set; }
[XmlElement(ElementName = "ALPHABETIC_CODE")]
public string Alpha_Code3 { get; set; }
[XmlElement(ElementName = "NUMERIC_CODE")]
public int NumCode { get; set; }
[XmlElement(ElementName = "MINOR_UNIT")]
public string MinorUnit { get; set; }
}
And this code, for deserialization:
public static XDocument Serialize<T>(this T source)
{
var ser = new XmlSerializer(source.GetType());
var sb = new StringBuilder();
using (var writer = new StringWriter(sb))
{
ser.Serialize(writer, source);
}
return XDocument.Parse(sb.ToString());
}
public static T Deserialize<T>(this XDocument xmlDocument)
{
var xmlSerializer = new XmlSerializer(typeof (T));
using (var reader = xmlDocument.CreateReader())
return (T) xmlSerializer.Deserialize(reader);
}
This is the XML source
But deserialization doesn't work. Please help.
Thanks!
I believe you only want to use XMLArray if you have a collection element for all of the items to sit underneath. For example here it could be ISO_CURRENCIES. I'm assuming you can't change the source in this case, so just use this instead:
[XmlElement("ISO_CURRENCY")]
public List<IsoCurrencyCode> IsoCodes { get; set; }
You should find that works.
Additionally, if you find you have further problems in getting the deserialization classes right, you can have them autogenerated for you from the XML and then you can take a look at the code that is created:
xsd source.xml
xsd source.xsd /c
This will create source.cs which you can then use in your project or adapt for your own uses.
As a further note, you'll find you can't use int for Minor_Unit as it's nullable (look at ANTARCTICA). You can't deserialize straight to an int?, so you'll either have to make it a string or go via another property, look at this question for more information.