Property will not serialize as XML attribute - c#

I am attempting to serialize a class as XML and to have the properties be serialized as attributes of the class, rather than a nested node. I am using WebApi to automatically handle the serialization of the XML.
This is my class:
[DataContract (Namespace="", Name="AttributeTest")]
[Serializable]
public class AttributeTestClass
{
[XmlAttribute("Property")]
[DataMember]
public int Property1 { get; set; }
}
Here is the output I am receiving (note that Property1 is not an attribute in spite of it being decorated with [XmlAttribute]):
<AttributeTest xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Property1>123</Property1>
</AttributeTest>
This is the output I want to receive:
<AttributeTest Property1="123" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
</AttributeTest>
What am I missing?

I'm not familiar with WebApi but the output you receive looks like it's serialized using DataContractSerializer, not XmlSerializer which you would need. Check if adding the following to Application_Start in Global.asax helps:
GlobalConfiguration.Configuration.Formatters.Clear();
GlobalConfiguration.Configuration.Formatters.Add(
new System.Net.Http.Formatting.XmlMediaTypeFormatter());
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
(From http://serena-yeoh.blogspot.de/2013/02/xml-serialization-in-aspnet-web-api.html)

Related

Parameter values are not mapped in Web API, if the Model class is marked as Serializable

I have a Web Api Controller as shown below
public class HomeController : ApiController
{
....
[HttpPost]
[Route("api/Add")]
public IHttpActionResult Add(Employee emp)
{
...
}
}
[Serializable]
public class Employee
{
public string Name { get; set; }
}
The parameters send in the Http Post request gets mapped in the Controller only when the [Serializable] attribute is removed from the Employee class. If I add the attribute, the value for the property Name in the Employee class is always null
Can someone explain whats going wrong, when the model class is marked as [Serializable] ?
To understand why this is happening, you need to understand the precise meaning of SerializableAttribute. It means This class can be serialized by serializing its public and private fields (i.e., not its properties). The attribute dates from c# 1.0, back when all properties had explicit backing fields, and was used initially by BinaryFormatter. So, when auto-implemented properties were introduced in c# 3.0, how were they to be serialized?
Not all serializers support [Serializable], but those that do serialize auto-implemented properties by serializing their hidden backing fields by name and value. You don't say what version of Web API you are using, nor the data format (XML or JSON), so you might be using:
DataContractSerializer. The data contract serializers support [Serializable], as stated in Types Supported by the Data Contract Serializer. The XML data contract serializer serializes your Employee class as follows:
<Employee>
<_x003C_Name_x003E_k__BackingField>My Name</_x003C_Name_x003E_k__BackingField>
</Employee>
DataContractJsonSerializer also serializes the backing fields, producing:
{"<Name>k__BackingField":"My Name"}
Json.NET will serialize the backing fields only if DefaultContractResolver.IgnoreSerializableAttribute = false:
var employee = new Employee { Name = "My Name" };
var json1 = JsonConvert.SerializeObject(employee, new JsonSerializerSettings { ContractResolver = new DefaultContractResolver { IgnoreSerializableAttribute = false } });
Console.WriteLine(json1);
Also produces {"<Name>k__BackingField":"My Name"}. Without the setting, Json.NET ignores the attribute.
XmlSerializer ignores [Serializable].
JavaScriptSerializer also ignores [Serializable].
The current version of Web API uses Json.NET and XmlSerializer, but this is configurable. Earlier versions use the data contract serializers or sometimes even JavaScriptSerializer.
Thus, the reason that your Name property is null is that you are using one of the serializers that support [Serializable] and you are posting JSON or XML that does not have the goofy backing field property name.
To work around this problem, you have the following options:
Annotate your class with data contract attributes. These supersede [Serializable] for all serializers that support it:
[Serializable]
[DataContract]
public class Employee
{
[DataMember]
public string Name { get; set; }
}
If you are using XML, be sure you are using XmlSerializer as specified in here.
If you are using JSON and Json.NET in MVC 4 Web API, you can set IgnoreSerializableAttribute = true in Global.asax.cs in Application_Start:
protected void Application_Start()
{
HttpConfiguration config = GlobalConfiguration.Configuration;
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver =
new DefaultContractResolver { IgnoreSerializableAttribute = true };
}
Or, just remove [Serializable], you really don't need it for anything other than BinaryFormatter.

web api list serialization

I want to serialize a list to xml (from a web-api method).
public class Result
{
public List<string> Users { get; set; }
}
So I get for example:
<result>
<user>Paul</user>
<user>David</user>
<user>Joan</user>
</result>
So far, I get:
<result>
<users>
<user>Paul</user>
<user>David</user>
<user>Joan</user>
</users>
</result>
How do I tell the serialization not to wrap the user list in a "users" tag?
Thanks.
You could either derive from XmlObjectSerializer and implement your own XML Serializer (see here FMI) or else manipulate your type so it works with the default formatter. Which isn't a great solution, but may work for a simple example, like so:
public class Result : List<User>
{
//Any user added to Result will be nested directly within Result in the XML
}
Further reading:
MSDN: XmlObjectSerializer Class:
"Extend the XmlObjectSerializer to create your own serializer to serialize and deserialize objects."
Insights on WCF: CustomXmlObjectSerializer: Real-world example with source code.
You need to replace default DataContractSerializer with XmlSerializer in Application_Start method.
For whole project:
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.UseXmlSerializer = true;
For specific type:
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.SetSerializer<Result>(new XmlSerializer(typeof(Result)));
After this you can use attributes to format your xml output:
public class Result
{
[XmlElement("user")]
public List<string> Users { get; set; }
}

Deserialize xml. xmlns='http//someaddress' was not expected

My problem is with deserialization of xml to c# objects. I have some class derived from some other class (I have the reason why I need to use inheritance in this place - doesn't matter why):
[Serializable]
[XmlType(TypeName = "OTA_HotelResRQ")]
public class ResRQ : OTA_HotelResRQ
{
}
in OTA_HotelResRQ I have declared namespaces and other information:
[Serializable]
[XmlTypeAttribute(Namespace = "http://www.opentravel.org/OTA/2003/05")]
[XmlRootAttribute(Namespace = "http://www.opentravel.org/OTA/2003/05", IsNullable = false)]
public class OTA_HotelResRQ : OtaRequestMessage, IRequest
And when I'm trying to serialize some request which looks like below:
<ns:OTA_HotelResRQ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ns="http://www.opentravel.org/OTA/2003/05"
PrimaryLangID="en" EchoToken="5613971064477293649" ResStatus="Commit" Version="2.1">
...SOME REQUEST WITH NS:....
<ns:POS><ns:/POS>
And now when I'm trying to deserialize this I have:
There is an error in XML document (3, 2). ---> System.InvalidOperationException: <OTA_HotelResRQ xmlns='http://www.opentravel.org/OTA/2003/05'> was not expected.
do you have some idea why I can't deserialize this? I can't modify base class for my model and also I need to have "ns" prefixes because service where I want to send that requires this format.
UPDATE:
My deserialization is implemented, that I'm getting bytes from string and try to deserialize using:
return (T) new XmlSerializer(typeof (T)).Deserialize(new MemoryStream(bytes));
I tried to fix it using solution provided by Charles and I updated my model:
[Serializable]
[XmlRoot("OTA_HotelResRQ", Namespace = "http://www.opentravel.org/OTA/2003/05")]
[XmlType("OTA_HotelResRQ", Namespace = "http://www.opentravel.org/OTA/2003/05")]
public class ResRQ : OTA_HotelResRQ
{
}
but still with no success. When I try to deserialize then I'm getting exception:
Types 'OTA_HotelResRQ' and
'ResRQ' both use the XML type name,
'OTA_HotelResRQ', from namespace 'http://www.opentravel.org/OTA/2003/05'. Use
XML attributes to specify a unique XML name and/or namespace for the type.
XML attributes are not inherited, so you need to add an XmlRoot attribute to your derived class:
[Serializable]
[XmlRoot("OTA_HotelResRQ", Namespace = "http://www.opentravel.org/OTA/2003/05")]
public class ResRQ : OTA_HotelResRQ
{
}

Error in xml document (1, 2) ... was not expected

I've tried several of the suggestions found on other SO answers for this same error but nothing has helped. Adding a namespace attr, using a blank namespace attr. Loading the string into an xmlDocument then rewriting it back to an xmlWriter. Using Serialazble instead of DataContract. Using xmlRoot attr with and without a namespace. Nothing works! Help.
I'm trying to instantiate a FillRequestExtended with the constructor that accepts an xml formatted string and always get the error Error in xml document (1, 2)
The Inner Exception reads: "http://schemas.datacontract.org/2004/07/WdRx.Exactus.Objects'> was not expected."
The class looks like so:
namespace WdRx.Exactus.Objects
{
[DataContract]
public class FillRequestExtended : IExtendedData
{
[DataMember]
[XmlElement]
public KeyValuePair<string, string>[] Properties { get; set; }
[DataMember]
[XmlElement]
public List<Payment> PaymentItems { get; set; }
public FillRequestExtended()
{
}
public FillRequestExtended(string xml)
{
FillRequestExtended extendedData;
XmlSerializer xs = new XmlSerializer(typeof(FillRequestExtended));
using (StringReader sr = new StringReader(xml))
{
XmlReader reader = XmlReader.Create(sr);
extendedData = (FillRequestExtended)xs.Deserialize(reader);
}
Properties = extendedData.Properties;
PaymentItems = new List<Payment>(extendedData.PaymentItems);
}
}
}
The string passed in is serialized elsewhere with no issue and looks like so:
<FillRequestExtended xmlns=\"http://schemas.datacontract.org/2004/07/WdRx.Exactus.Objects\"
xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
<PaymentItems>
<Payment>
<Amount>-43.95</Amount>
<Summary>CCP PAYMENT - AUTH:014910</Summary>
</Payment>
<Payment>
<Amount>0.00</Amount>
<Summary>Type: VIS Account: ************5793 Expires: 05/16 Authorization: 014910</Summary>
</Payment>
</PaymentItems>
<Properties xmlns:a=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">
<a:KeyValuePairOfstringstring>
<a:key>RxDcDate</a:key>
<a:value>20150414</a:value>
</a:KeyValuePairOfstringstring>
<a:KeyValuePairOfstringstring>
<a:key>RefillStatus</a:key>
<a:value>PROFILED</a:value>
</a:KeyValuePairOfstringstring>
</Properties>
</FillRequestExtended>
There are at least two different XML deserializers. By decorating the class with [DataContract] you are forcing it to use the WCF serializer. In the deserialize code, you are using the normal XML object deserializer.

Deserialize XML Array Where Root is Array and Elements Dont Follow Conventions

The XML I am getting is provided by an outside source so I don't have the ability to easily reformat it. I would like to use xml attributes on my entities instead of having to write a linq query that knows how the XML and entity is formatted. Here is an example:
<?xml version="1.0"?>
<TERMS>
<TERM>
<ID>2013-2</ID>
<DESC>Spring 2013</DESC>
</TERM>
<TERM>
<ID>2013-3</ID>
<DESC>Summer 2013 Jun&Jul</DESC>
</TERM>
</TERMS>
I know the the XMLSerializer expects ArrayOfTerm instead of TERMS for example, but that I can tweak my entity to use a different element name with the xml attributes such as this:
public class TermData
{
[XmlArray("TERMS")]
[XmlArrayItem("TERM")]
public List<Term> terms;
}
public class Term
{
[XmlElement("ID")]
public string id;
[XmlElement("DESC")]
public string desc;
}
and I am deserializing the data like so:
TermData data;
XmlSerializer serializer = new XmlSerializer(typeof(TermData));
using (StringReader reader = new StringReader(xml))
{
data = (TermData)serializer.Deserialize(reader);
}
return View(data.terms);
The problem I am facing is that TERMS is the root and the array itself. If the XML were to have a root element that was not the array, I could edit my TermData class like so and it would deserialize correctly (already tested).
[XmlRoot("ROOT")]
public class TermData
{
[XmlArray("TERMS")]
[XmlArrayItem("TERM")]
public List<Term> terms;
}
Note that using TERMS as the XMLRoot does not work. Right now, my code is throwing
InvalidOperationException: There is an error in XML document (2,2).
InnerException: "<TERMS xmlns=" was not expected.
This would lead me to believe that the XML is not formatted correctly, but from my understanding the example I gave is perfectly valid XML.
This would all be trivial if I could edit the source xml, but there could be tons of other responses like this and I need to be able to flex for whatever I might get. What I'm trying to confirm is whether or not the XMLSerializer can support this type of XML structure. I've tested just about everything and can't get it deserialize without editing the XML. It would also be convenient if I didn't have to define a wrapper class (TermData) to hold the list, but this seems to only work if the xml follows the naming conventions for the serializer (ArrayOfTerm, etc).
Maybe you can try :
[XmlRoot("TERMS")]
public class TermData
{
public TermData()
{
terms = new List<Term>();
}
[XmlElement("TERM")]
public List<Term> terms{get;set;}
}
public class Term
{
[XmlElement("ID")]
public string id{get;set;}
[XmlElement("DESC")]
public string desc{get;set;}
}
Hope this will help,

Categories

Resources