I have a DTO and a Domain project in my solution and an MVC front end with web api to expose data.
I have the web api controller set up and the action is getting my DTO object back from the DataService. That's all great, however, I want xml to be returned and I want some of the values to be in xml attributes e.g.
<root xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<lookups>
<lookup category="General" field="Alert Type" value="Lack of Transparency" entityid="2273"/>
<lookup category="General" field="Alert Type" value="Unfair Terms " entityid="2274"/>
<lookup category="General" field="Alert Type" value="Operator Concerns" entityid="2275"/>
...
</lookups>
<paymentmethods />
<affiliates />
</root>
The Lookup class is as follows:
[Serializable]
[XmlSerializerFormat]
public class Lookup
{
[XmlAttribute("category")]
public String Category { get; set; }
[XmlAttribute("field")]
public String Field { get; set; }
[XmlAttribute("value")]
public String Value { get; set; }
[XmlAttribute("entityid")]
public String EntityId { get; set; }
public Lookup(String Category, String Field, String Value, int? EntityId = null)
{
this.Category = Category;
this.Field = Field;
this.Value = Value;
this.EntityId = (EntityId != null ? EntityId.ToString() : null);
}
public Lookup() { }
}
initially I had my DTO objects (e.g Lookup, PaymentMethod and Affiliates) with my viewmodels, but moved them into my DTO project.
I have set UseXmlSerializer = true in my global.asax
Before moving the objects from hy viewmodels folder to the DTO project, it was working and I was getting the desired XML. AFTER moving, it appears to be ignoring the XmlSerializerFormat and using DataContractSerliazer.
So using DataMember attributes, I can format the xml, but obviously I can't set some properties to be serialised as xml attributes
Any thoughts on why it seems to be ignoring the [XmlSerializerFormat] and [XmlAttribute("field")] attributes?
I've read a few SO posts like:
XmlSerializer ignores [XmlAttribute] in WebApi and
How can you control .NET DataContract serialization so it uses XML attributes instead of elements?
I found the solution to this here:
http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization#xml_pertype
Setting the xml Serializer in the Global.asax to work on a specific type worked for me :
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.UseXmlSerializer = true;
xml.SetSerializer<FullResponseRoot>(new XmlSerializer(typeof(FullResponseRoot)));
where FullResponseRoot is the name of the object that I am serialising (the classes mentioned above are properties of FullResponseRoot)
FullResponseRoot forms the root node of the generated xml
Related
I am trying to deserialize an xml to an object to use. We have created templates and would like to keep the xml the same standard if possible. The problem I am trying to figure out is how to look within a standard node in the xml and all subnodes are the same object type, just with different node names.
For example:
<Account>
<AccountNumber>12345</AccountNumber>
<Balance>12.52</Balance>
<LateFee>0</LateFee>
</Account>
The Account level is always within the template, but everything below that is variable. Is there a way to deserialize all nodes within the Account level to be the same object?
Public Class AccountNode
{
Public String Name { get; set; }
Public String Value { get; set; }
}
Based on my research, it appears, they have to have a standard naming schema and then you can have an attribute to assign to the Name value. I just haven't been able to confirm that. If someone has a link that I haven't been able to find, or is knowledgeable and can confirm whether or not this is a possibility, I would like to know.
EDIT:
I have a much larger xml than listed above, so I'm trying to see how I can deserialize it.
<AccountNumber>
<KeyWord Name="Customer Account" isRegex="False" errorAllowance="10" LookFor="Customer Account">
<Rectangle>
<Left>200</Left>
<Bottom>350</Bottom>
<Right>600</Right>
<Top>690</Top>
</Rectangle>
<Relations KwName="Charges">
<Relation>above.0</Relation>
</Relations>
</KeyWord>
<Capture DataType="String" FindIfKeywordMissing="false">
<Rectangle>
<Left>200</Left>
<Bottom>350</Bottom>
<Right>600</Right>
<Top>690</Top>
</Rectangle>
<Relations anchorName="ChargeSection">
<Relation>rightOf.0</Relation>
<Relation>below.-20</Relation>
<Relation>above.20</Relation>
<Relation>width.150</Relation>
</Relations>
<Regex>Customer account\s+(\S+)</Regex>
</Capture>
</AccountNumber>
So with this one, I assume it is similar, but basically the Account Number node is the variable one and everything above and below it is standard.
You could use a surrogate [XmlAnyElement] public XElement[] AccountNodesXml property in your Account class to manually convert your AccountNode objects from and to XML nodes. Marking the property with XmlAnyElement ensures that the elements will taken verbatim from the XML:
public class Account
{
public Account() { this.AccountNodes = new List<AccountNode>(); }
[XmlIgnore]
public List<AccountNode> AccountNodes { get; set; }
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[XmlAnyElement]
public XElement[] AccountNodesXml
{
get
{
if (AccountNodes == null)
return null;
return AccountNodes.Select(a => new XElement((XName)a.Name, a.Value)).ToArray();
}
set
{
if (value != null)
AccountNodes = value.Select(e => new AccountNode { Name = e.Name.LocalName, Value = (string)e }).ToList();
}
}
}
Sample fiddle which successfully deserialized and re-serializes the following XML:
<Account>
<AccountNumber>12345</AccountNumber>
<Balance>12.52</Balance>
<LateFee>0</LateFee>
</Account>
How do I get the SOAP xml attribute value in a wcf service?
<ns3:NotifRQ Status="Commit"
xmlns:ns2="http://www.dddd.com/df/dd/"
xmlns:ns3="http://www.dd.org/OTA/">
<ns3:rev>dfdfkkl</ns3:rev>
<ns3:change>dfdfkkl</ns3:change>
</ns3:NotifRQ>
This is the code I have now for the data contract:
[DataContract(Name = "NotifRQ", Namespace = "http://www.dd.org/OTA/")]
public class NotifRQ
{
[DataMember(Name = "Status")]
public string ResStatus;
}
Your Status attribute needs to be a field or property of the NotifRQ class and you need to instruct WCF to use the less optimal XmlSerializer instead of the DatacontractSerializer as explained here. You achieve that by using the XmlSerializerFormat attribute on your class.
You can now apply XmlAttribute to a field or property of your class that gets or sets the value of an attribute on the xml element.
Create and annotate your class as follows:
[DataContract(Namespace="http://www.dd.org/OTA/")]
[XmlSerializerFormat]
public class NotifRQ
{
[DataMember, XmlAttribute]
public string Status="Commit";
[DataMember]
public string rev;
[DataMember]
public string change;
}
Above class will write and read the following wire-format:
<?xml version="1.0" encoding="utf-16"?>
<NotifRQ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
Status="Commit">
<rev>foo</rev>
</NotifRQ>
I'm using DataContractSerializer in my Web API application and in my action I'm returning a Data Type as below:
public class Event
{
public string Name {get; set;}
public IList<Division> Divisions {get;set;}
}
When serialized it's returning the below xml:
<Event xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07
/EventTypeNameSpace">
<Name>some name</Name>
<Divisions i:nil="true" />
</Event>
1) Why is it returning two xmlns:i and xmlns attributes? how can they be excluded?
2) How can I exclude the Divisions from the xml when it's null?
1: the "http://schemas.datacontract.org/2004/07" is the default namespace used by types serialized by data-contract serializer; if you don't like that - change your contract; the "http://www.w3.org/2001/XMLSchema-instance" defines "nil" as a special value
2: by defining the contract properly
[DataContract(Namespace="")]
public class Event
{
[DataMember]
public string Name { get; set; }
[DataMember(EmitDefaultValue=false)]
public IList<Division> Divisions { get; set; }
}
However: I should add - if you want tight control over what the layout looks like, you should probably be using XmlSerializer, not DataContractSerializer
My web api is returning a set of objects which are differ from the Domain object. Forexample, I my domain has an Employee class but I don't want to expose all the members of the Employee class in my api so I created another class called EmployeeApiModel.
Now my WebApi is returning a List of EmployeeApiModel but I want to be able to specify the name to which it should serialize to. That is instead of <EmployeeApiModel> tag in the xml, I want to get <Employee> but without changing the fact that the underlying class which is being serialized is EmployeeApiModel.
How can I achieve this?
Technically, Web Api support both json and xml based on content negotiation mechanism, Json is the default format, if you want to receive xml, just put on header:
Accept: application/xml
To understand more content negotiation, access this
Since you want your api support both json and xml, you should use DataContract and DataMember Attribute for serialization for your model: EmployeeApiModel, something like:
[DataContract(Name = "Employee")]
public class EmployeeApiModel
{
[DataMember(Name = "Name2")]
public string Name { get; set; }
[DataMember]
public string Email { get; set; }
}
See more on this blog-post
You can control the output of your serialized XML by using various Attribute tags.
[XmlRoot("Employee")]
Public class EmployeeApiModel
{
[XmlElement("fname")]
public string FirstName { get; set; }
public string LastName { get; set; }
public int age { get; set; }
}
this will produce XML like:
<Employee>
<fname>John</fname>
<LastName >Smith</LastName >
<age>24</age>
</RootElementsName>
You can read more about the various XML modifiers here: http://msdn.microsoft.com/en-us/library/e123c76w.
If you want to use existing XML modifiers for JSON, check out this post: Serialize .Net object to json, controlled using xml attributes
I am experimenting with using xml as a database for small CMS, like gallery or staff profiles
etc
however being all subsonic minded i am stuck on how i bind my xml document to a modelclass
so that i can then use that class for strongly typed views:
here is my model class:
[XmlRoot("employee")]
public class EmployeesModel
{
[Required]
[DisplayName("Name: ")]
[XmlElement("employeeName")]
public string employeeName { get; set; }
[Required]
[DisplayName("Position: ")]
[XmlElement("employeePosition")]
public string employeePosition { get; set; }
[Required]
[DisplayName("Description")]
[XmlElement("employeeDescription")]
public string employeeDescription { get; set; }
[Required]
[DisplayName("Photo: ")]
[XmlElement("employeePhoto")]
public string employeePhoto { get; set; }
[Required]
[DisplayName("ID: ")]
[XmlElement("employeeID")]
public int employeeID { get; set; }
}
and here is my code:
XDocument xmlDoc = XDocument.Load(Server.MapPath("~/App_Data/employees.xml"));
var model = (from xml in xmlDoc.Descendants("employee")
select xml) as IEnumerable<EmployeesModel>;
return View(model);
the XML
<?xml version="1.0" encoding="utf-8" ?>
<employees>
<employee>
<employeeName>David parker</employeeName>
<employeePosition>Senior Web Developer</employeePosition>
<employeeDescription>This is a test description<br>feele free to add something here.</employeeDescription>
<employeePhoto>mypic.jpg</employeePhoto>
<employeeID>1</employeeID></employee></employees>
the xml side work but model is always empty, however i get no runtime erros when trying to bind, i know there is more i should do here but i need some help.
for clarity i am using asp.net mvc 2 rc 2
thanks
You need to deserialize the XML into objects. You cannot simply cast XML as objects. When you say as IEnumerable<EmployeesModel>, you'll get a null since the types aren't compatible. Your code could look something like this:
var serializer = new XmlSerializer(typeof(EmployeesModel));
var model =
from xml in xmlDoc.Descendants("employee")
select serializer.Deserialize(xml.CreateReader()) as EmployeesModel;
Another option you could consider is to project the XElements into EmployeesModel objects, like this:
var model =
from xml in xmlDoc.Descendants("employee")
select new EmployeesModel {
employeeName = (string)xml.Element("employeeName"),
employeePosition = (string)xml.Element("employeePosition"),
employeeDescription = (string)xml.Element("employeeDescription"),
employeePhoto = (string)xml.Element("employeePhoto"),
employeeID = (int)xml.Element("employeeID"), };
As you can see, that can get tedious. However, it may be appropriate. If your XML file represents all employee data but your view shows only a subset of the data or the data in a different structure, you probably don't want your view model to be a direct copy of the contents of your data store.
If you want make binding xml to model class, you can use templay on codeplex.
Further you can do some process on your model class and their childrens.
https://templay.codeplex.com/