Serialize collection without root tag - asp.net-web-api

I'm attempting to setup a Web API endpoint that has a specific requirement that the XML format be similar to this:
<broadcast>
<name></name>
<description></description>
<episode>
<title>&lt/title>
</episode>
<episode>
<title>&lt/title>
</episode>
...
</broadcast>
My models look like this:
[DataContract]
public class broadcast
{
[DataMember]
public string name { get; set; }
[DataMember]
public string description { get; set; }
[DataMember]
public List<episode> episodes { get; set; }
}
[DataContract]
public class episode
{
[DataMember]
public string title { get; set; }
}
The problem I'm running into is that the episode items get put into a container tag <episodes>. Is there any way to serialize the episodes list so that the container tag doesn't appear?

As it turns out there is a way to do this, but you must use the XmlSerializer. To do this add the following line to to your WebApiConfig.cs
config.Formatters.XmlFormatter.UseXmlSerializer = true;
Then add the [XmlElement] attribute to any collections you don't have to have a root tag. If you want to have a root tag use [XmlArray]. So in my example above:
[XmlType]
public class broadcast
{
[XmlElement]
public string name { get; set; }
[XmlElement]
public string description { get; set; }
[XmlElement] // could use [XmlArray] if I want a root tag
public List episodes { get; set; }
}
[XmlType]
public class episode
{
[XmlElement]
public string title { get; set; }
}

Kyle's answer is almost correct. You need [XmlElement("episode")] for this to work.

As far as I know removing the root element in a collection type is not possible. This is subject of Collection Type Serialization and even though there are multiple options to alter how collections are serialized using attributes like CollectionDataContractAttribute there isn't an option to remove the root for the Serialized collection element.

You can use MessageContract instead DataContract. Message contracts describe the structure of SOAP messages sent to and from a service and enable you to inspect and control most of the details in the SOAP header and body:
[MessageContract]
public class broadcast
{
[MessageBodyMember]
public string name { get; set; }
[MessageBodyMember]
public string description { get; set; }
[MessageBodyMember]
public List<episode> episodes { get; set; }
}
Here you can find more information and details --> https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/using-message-contracts

Related

Bind attribute or ViewModel?

I'm learning about the Bind attribute and I have a doubt.
I can use the Bind attribute to include/exclude the data that will be posted, so.
Would it not be better to use a specific ViewModel instead of the Bind attribute?
Think about what happened if your entity changes overtime, then you might force to change all your different viewModels which you have created instead of using Include or Exclude. it will get hard to maintain your code.
Suppose you have this :
public class PersonalViewModel
{
private int PersonalID { get; set; }
public string PersonalName { get; set; }
public string PersonalFamily { get; set; }
public byte? GenderID { get; set; }
public string PersonalPhone { get; set;}
}
Consider these :
public string ShowPersonalToAll(
[Bind(Exclude = "PersonalPhone")]PersonalViewModel newPersonal)
{...}
OR
public class PersonalViewModel
{
private int PersonalID { get; set; }
public string PersonalName { get; set; }
public string PersonalFamily { get; set; }
public byte? GenderID { get; set; }
}
Now What if saving personal's mobile become important! and if you have created different customized ViewModel for several action (depends on application's business)?
Then you have to change the main ViewModel and all the other Customize ViewModel, While by using Exclude there is no need to change ViewModels, no need to change actions and the main ViewModel just changes.

Problems with Model binding and validation attributes with asp.net Web API

I am writing a Web API with ASP.NET Web API, and make use of the following View Model.
I seem to be having a problem with the data binding when there are two validation attributes on a particular property (i.e. [Required] and [StringLength(10)]).
When posting a JSON value from a client to a controller action of the form:
// POST api/list
public void Post([FromBody] TaskViewModel taskVM)
I observe the following:
If I remove one of the multiple attributes everything is bound OK;
If I leave in the multiple attributes, the client recieves a 500 internal server error and the body of the Post method is never reached.
Any ideas why this happens?
Cheers
public class TaskViewModel
{
//Default Constructor
public TaskViewModel() { }
public static TaskViewModel MakeTaskViewModel(Task task)
{
return new TaskViewModel(task);
}
//Constructor
private TaskViewModel(Task task)
{
this.TaskId = task.TaskID;
this.Description = task.Description;
this.StartDate = task.StartDate;
this.Status = task.Status;
this.ListID = task.ListID;
}
public Guid TaskId { get; set; }
[Required]
[StringLength(10)]
public string Description { get; set; }
[Required]
[DataType(DataType.DateTime)]
public System.DateTime StartDate { get; set; }
[Required]
public string Status { get; set; }
public System.Guid ListID { get; set; }
}
You need to inspect what is inside in the 500 internal server
make sure that you turn customerror off in your web.config
If you selfhost web.API you need to set GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
use your browser development console's network tab (in IE, Chrome you can get the console with F12) or if you are using FireFox then use FireBug or a thrid party tool like Fiddler.
Then you can see what went wrong on the server and go further to solve your problem.
In your case this is in the response:
"Message":"An error has occurred.","ExceptionMessage":"Property
'StartDate' on type 'MvcApplication3.Controllers.TaskViewModel' is
invalid. Value-typed properties marked as [Required] must also be
marked with [DataMember(IsRequired=true)] to be recognized as
required. Consider attributing the declaring type with [DataContract]
and the property with
[DataMember(IsRequired=true)].","ExceptionType":"System.InvalidOperationException"
So your problem is not that you have two attributes but that you've marked your properties with [Required] to solve this the exception tells you what to do.
You need to add [DataMember(IsRequired=true)] to your required properties where the property type is a value type (e.g int, datatime, etc.):
So change your TaskViewModel to:
[DataContract]
public class TaskViewModel
{
//Default Constructor
public TaskViewModel() { }
[DataMember]
public Guid TaskId { get; set; }
[Required]
[DataMember]
[StringLength(10)]
public string Description { get; set; }
[Required]
[DataMember(IsRequired = true)]
[DataType(DataType.DateTime)]
public System.DateTime StartDate { get; set; }
[Required]
[DataMember]
public string Status { get; set; }
[DataMember]
public System.Guid ListID { get; set; }
}
Some side notes:
You need to reference the System.Runtime.Serialization dll in order to use the DataMemberAttribute
You need to mark your class with [DataContract] and you need to mark all of its properties with [DataMember] not just the required ones.

No XmlDocument and XDocument in VS2010 Express for Windows Phone

I use MS Studio 2010 Express for Windows Phone to build the app. In my app, I got the http respond and read it in string then I deserializer into the class object. It works fine in the class without List collection as a property. When it deserializer the class with List property, it got the error like this
Error in line 12 position 5. Expecting state 'Element'.. Encountered 'EndElement' with name 'ContactList', namespace 'http://schemas.datacontract.org/2004/07/DataObjects’.
I think I can read the xml and assign the value into the class. I searched to read xml file and a lot of website mentions to use XDocument .Prase method or xlmDocument.Load. However event adding the System.Xml.Ling as reference , I still cannot see XDocument .Parse method or xlmDocument . Would someone tell me what I should do in order to assign the following value into the class?
There is my class object:
public class CallDetails
{
public int id { get; set; }
public string summary { get; set; }
public string errorMsg { get; set; }
public int parentCallid { get; set; }
public string parentCallURL { get; set; }
public string assignedTo { get; set; }
public string OrgName { get; set; }
public DateTime onHoldSince { get; set; }
public DateTime onHoldUntil { get; set; }
public string requester { get; set; }
public bool isOnHold { get; set; }
private List<Contact> m_ContactList = new List<Contact>();
public List<Contact> ContactList
{
get { return m_ContactList; }
}
}
There is the respond:
<?xml version="1.0" encoding="utf-8"?><CallDetails xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/DataObjects">
<ContactList>
<Contact>
<Method>PriPhone</Method>
<Number>(604) 555-1234</Number>
</Contact>
<Contact>
<Method>Fax</Method>
<Number>(604)555-1234</Number>
</Contact>
</ContactList>
<errorMsg
i:nil="true" />
<id>0</id>
<isOnHold>false</isOnHold>
<onHoldSince>0001-01-01T00:00:00</onHoldSince>
<onHoldUntil>0001-01-01T00:00:00</onHoldUntil>
<parentCallURL>/Call/349551</parentCallURL>
<parentCallid>0</parentCallid>
<requester>Peter </requester>
<summary>Mobile Application Research</summary>
</CallDetails>
Just adding the reference to System.Xml.Linq won't do it, you also need to reference the namespace in the class header:
using System.Xml.Linq;
Then you can call something like XDocument doc = XDocument.Parse(content);. However, from what I can tell, you are trying to deserialize data, so you might want to use the XmlSerializer class instead for all the core work. You can find some code 'ispiration' here.
I found it that the CallDetail Class cause error. It should be like that
public List<Contact> ContactList { get; set; }

How can I mark properties required to show up different in intellisense

Let's say we have a class like so:
public class Plan
{
public string PlanCode { get; set; } //Required
public string Name { get; private set; }
public string Description { get; set; }
public string SuccessUrl { get; set; }
}
Is there a way to make intellisense show required fields/properties different, such as italicized or a shade of red?
One would be able to determine what properties would be required quickly if calling a Create() method for instance.
Note: When I say show up different in intellisense I don't mean the tool-tip text that you see when the member is highlighted. I specifically mean the text of the member itself.
No, I don't believe you can. You should design your objects with required fields in mind, i.e. constructors.
So any fields that the object requires should be parameters in a constructor.
public class Plan
{
public string PlanCode { get; set; } //Required
public string Name { get; private set; }
public string Description { get; set; }
public string SuccessUrl { get; set; }
public Plan(string planCode)
{
PlanCode = planCode;
}
}
This is allows your code to clearly express its intention. Any developer using the Plan class will know that PlanCode is required, as it is enforced by the constructor.

Entity Framework 4.1 - Segregating an entity into multiple tables (Code-First)

I'm trying to find out how I can re-use a simple 'Comment' entity type for multiple scenarios where something is 'commentable' in my application.
At the moment, I have a couple of entities that a user is able to post comments to. Examples include Blogs, Profiles and Photos - these can all be 'commented' on.
I'd like to be able to use the same 'Comment' class for each of these scenarios, but I don't want to end up with one HUGE table full of comments for everything. I figure it would be much more efficient to at least store a table of BlogComments, PhotoComments, and ProfileComments. At the moment, my Comment class looks like this:
public class Comment
{
[Key]
public int Id { get; set; }
public int ContextId { get; set; }
[StringLength(256)]
public string Content { get; set; }
public DateTime DatePosted { get; set; }
public virtual Member Author { get; set; }
}
Presumably, I'd need the 'ContextId' field to refer to the particular thing being commented on. This Id might be the Id of a Blog, a Profile or a Photo. I was hoping to be able to refer to comments much like a normal ICollection in these classes, and I have some code like this for the Photos as an example:
public class Photo
{
[Key]
public int Id { get; set; }
[StringLength(48)]
public string FileName { get; set; }
public virtual Member Owner { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
I've been pointed to various articles during my searches, but none really seem relevant to my particular situation. How can I map these comment collections to different tables, and avoid having a comment "super-table"?
Any help, pointers or advice would be hugely appreciated :)
You can create an abstract Comment class and inherit from it specific comments such as PhotoComment, ProfileComment. You will be able to map the comments to different tables.
public abstract class Comment
{
[Key]
public int Id { get; set; }
[StringLength(256)]
public string Content { get; set; }
public DateTime DatePosted { get; set; }
public virtual Member Author { get; set; }
}
public class PhotoComment : Comment
{
public int PhotoId { get; set; }
public virtual Photo Photo { get; set; }
}
public class Photo
{
[Key]
public int Id { get; set; }
[StringLength(48)]
public string FileName { get; set; }
public virtual Member Owner { get; set; }
public virtual ICollection<PhotoComment> Comments { get; set; }
}

Resources