I have a WCF REST service that is returning a business object that contains a string field called 'GeoDataKML'. This field is just a string value, but it has KML string inside it (encoded, so that it doesn't interfere with XML serialization of this business object). The problem is that when this field has data in it, i get a WCF error trying to download this data:
System.Runtime.Serialization.SerializationException
Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
The data i'm trying to send is below. If i blank out the GeoDataKML value, then the service works fine, no errors. Applying KnownType doesn't seem to do any good. Any ideas on how to get WCF REST to NOT try to serialize the data in this string object?
<?xml version="1.0" encoding="Windows-1252"?><ArrayOfFullLogEntry xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<FullLogEntry>
<IsSelected>false</IsSelected>
<StaticGUID>be547685-2bdd-4abf-84a4-d20c13c5fe58</StaticGUID>
<GeoDataKML><?xml version="1.0" encoding="Windows-1252"?><kml xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.opengis.net/kml/2.2"><Placemark id="7e5272b6-d1b4-4128-95bf-24ea9358681a"><name>CasualtyTool</name><styleUrl>#CasualtyTool</styleUrl><Point><altitudeMode>clampToGround</altitudeMode><coordinates>-115.594205424414,41.295597016765,5000</coordinates></Point></Placemark></kml></GeoDataKML>
<SymbolGUID>00000000-0000-0000-0000-000000000000</SymbolGUID>
<GeoDataTypeGUID>00000000-0000-0000-0000-000000000000</GeoDataTypeGUID>....
FullLogEntry only has these values:
public class FullLogEntry
{
public Guid StaticGUID {get;set;}
public string GeoDataKML {get;set;}
public Guid GeoDataTypeGUID{get;set;}
public Guid SymbolGUID{get;set;}
public bool IsSelected {get;set;}
}
service contract is just:
[ServiceContract(Namespace = WCFServiceBaseClass.ServiceNamespace)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, Namespace = WCFServiceBaseClass.ServiceNamespace)]
where the namespace is 'http://test.org'
needed to add
<System.Runtime.Serialization.KnownType(GetType(PlacemarkType))> _
at the top of the fulllogentry class. works now!
Related
I have already spent two days trying to find a solution to my problem and no results, so I would appreciate any hints.
What I am trying to achieve:
Briefly, I have created and published a WCF service (RESTful web services) with one method GetUsers that should return something like this:
[{"Name":"John","Surname":"Dell","GroupName":"Operator"}, {"Name":"John1","Surname":"Hp","GroupName":"Operator"}, {"Name":"John2","Surname":"Apple","GroupName":"Operator"}]
Unfortunately, when I test this method in the browser (http://localhost:28099/TestAPI.svc/GetUsers?inputName=Company, I get the response which includes the escape backslash and the double quotes at the beginning and at the end of the result, like below:
"[{\"Name\":\"John\",\"Surname\":\"Dell\",\"GroupName\":\"Operator\"}, {\"Name\":\"John1\",\"Surname\":\"Hp\",\"GroupName\":\"Operator\"}, {\"Name\":\"John2\",\"Surname\":\"Apple\",\"GroupName\":\"Operator\"}]"
The application consuming this webservice is telling me that this is not a valid JSON format.
In my web service method, I am getting the initial data in XML format, passing it to a datatable object, and then trying to convert it to JSON using the JSON.NET.
Below is the source XML:
<Root>
<row Name ="John" Surname="Dell" GroupName="Operator"/>
<row Name ="John1" Surname="Hp" GroupName="Operator"/>
<row Name ="John1" Surname="Apple" GroupName="Operator"/>
</Root>
Below is a snippet from my WCF code:
[ServiceContract]
public interface IWebServiceTest
{
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
String GetUsers(string inputName);
}
public class TestWCF: IWebServiceTest
{
Public String GetUsers(string inputName)
{
...................
StringReader stringReader1 = new StringReader(responseXMLResult.ToString());
XmlReader reader = XmlReader.Create(stringReader1);
ds.ReadXml(reader);
string json = JsonConvert.SerializeObject(ds.Tables[0], Newtonsoft.Json.Formatting.None);
return json
}
As Evk said, WCF has serialized the value you return to JSON, we don't need to use JSON NET anymore. In WCF we use DataContract to serialize objects. Here is a demo:
[ServiceContract]
public interface IService
{
[OperationContract]
[WebGet(ResponseFormat =WebMessageFormat.Json,BodyStyle =WebMessageBodyStyle.Bare)]
Person GetUsers();
}
This is the service interface, we set ResponseFormat to json.
[DataContract]
public class Person
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Surname { get; set; }
[DataMember]
public string GroupName { get; set; }
}
This is the Person object we want to transfer, we use DataContract to serialize the object.
public class Service : IService
{
public Person GetUsers()
{
Person person = new Person();
person.Name = "wwww";
person.Surname = "Dell";
person.GroupName = "Operator";
return person;
}
}
This is the implementation class of the method, we return the person object directly, and WCF will help us serialize this object into json and return it to the client.
I have a wcf restful service on a IIS server.
I have made some API, which can be called sending both xml or json.
I've made my C# classes and then, I'm testing it. With JSON is perfect, but I have still some issues with XML request.
I want to send the xml with a post and this is the xml I send:
<?xml version="1.0" encoding="utf-8" ?>
<SetClientiXML
xmlns="http://tempuri.org/">
<dati>
<ArrayOfWrapClienti
xmlns="http://schemas.datacontract.org/2004/07/MultipayOnline"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<WrapClienti>
<CODRETE>0018</CODRETE>
<CODICE>20685</CODICE>
<NOME>A.T.E.R. Azienda Territoriale</NOME>
<INDIRIZZO>PIAZZA POZZA</INDIRIZZO>
<CITTA>Verona</CITTA>
<CAP>37123</CAP>
<PROV>VR</PROV>
<CODICEFISCALE>00223640236</CODICEFISCALE>
<PIVA>223640236</PIVA>
<EMAIL/>
<ESPOSIZ_CONTABILE>937,02</ESPOSIZ_CONTABILE>
<STATO>FALSE</STATO>
</WrapClienti>
</ArrayOfWrapClienti>
</dati>
<retista>3303903</retista>
<hashedString>oklkokokokok</hashedString>
</SetClientiXML>
the wcf read well "retista" and "hashedString", but "dati" is empty (0 elements), while I expect it has got the "wrapClienti" object I sent.
This is the prototype of my API:
[OperationContract]
[WebInvoke(UriTemplate = "SetClienti.xml", Method = "POST", BodyStyle=WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Xml)]
GenericResponse SetClientiXML(List<WrapClienti> dati, string retista, string hashedString);
So, the problem is that the List is empty.. why? How can I write the xml to make readable the list?
Ask me if I can give to you more details.
UPDATE: More weird!!
With this xml:
<?xml version="1.0" encoding="utf-8" ?><SetClientiXML xmlns="http://tempuri.org/">
<dati>
<WrapClienti xmlns="http://schemas.datacontract.org/2004/07/MultipayOnline" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<CODRETE>0018</CODRETE>
<CODICE>20685</CODICE>
<NOME>A.T.E.R. Azienda Territoriale</NOME>
<INDIRIZZO>PIAZZA POZZA</INDIRIZZO>
<CITTA>Verona</CITTA>
<CAP>37123</CAP>
<PROV>VR</PROV>
<CODICEFISCALE>00223640236</CODICEFISCALE>
<PIVA>223640236</PIVA>
<EMAIL/>
<ESPOSIZ_CONTABILE>937,02</ESPOSIZ_CONTABILE>
<STATO>FALSE</STATO>
</WrapClienti>
</dati>
<retista>3303903</retista>
<hashedString>oklkokokokok</hashedString>
</SetClientiXML>
the wcf read some attributes of the List, and other.. are nul!!!
I my WrapClienti I have a lof of attributes. Two of them are:
private string p_CAP { get; set; }
public string CAP
{
get
{
if (model == null)
return p_CAP.ToSafeString();
else
return this.model.CAP.ToSafeString();
}
set { p_CAP = value; }
}
private string p_PROV { get; set; }
public string PROV
{
get
{
if (model == null)
return p_PROV.ToSafeString();
else
return this.model.PROV.ToSafeString();
}
set { p_PROV = value; }
}
the problem is, with the xml above and with two breakpoint on the two set methods, only the set of PROV is called and, the one of CAP, not!!! Why? Now I'm really getting crazy... why this behavior??
Solution here.
This has to do with the ordering of the fields in your XML. It sounds very strange, but WCF DataContractSerializer is really fussy about the order in which the fields are encountered in the XML, but even worse, also in comparison to how they are defined in the code.
You see, the serializer wants the fields to be defined in alphabetical order, and if you serialize an instance of your class, you will find that the resulting XML fields are in alphabetical order. However, on deserialization, the serializer finds that the type you want to deserialize to has the fields defined in the "wrong" order. In this situation the behavior can seem random, but I think it has something to do with the fact that CAP should be the first field encountered, whereas PROV should be the last field, alphabetically.
So you have two options:
Reorder your XML and the fields in your class to be in alphabetical order, or
Decorate your class members with the DataMemeber property, and define the order of serialization.
You can do 2 like this:
[DataContract]
public class WrapClienti
{
[DataMember(Order=1)]
public string CAP { get; set; }
[DataMember(Order=2)]
public string PROV { get; set; }
...etc
}
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)
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,
I'm writing a WCF webservice and passing in a complex type as a parameter of the method. The complex type looks like this:
[DataContract(Namespace = "")]
public class MyRequest
{
[DataMember()]
public string TransactionId { get; set; }
[DataMember(IsRequired = true)]
public bool IsRollback { get; set; }
[DataMember(IsRequired = true)]
public OrderType OrderType { get; set; }
[DataMember(IsRequired = true)]
public ICustomerId CustomerId { get; set; }
[DataMember()]
public long OrderId { get; set; }
[DataMember()]
public AnotherComplexType PurchaseInfo { get; set; }
The webservice method looks like this:
[ServiceKnownType(typeof(CustomerIdByName))]
[ServiceKnownType(typeof(CustomerIdByAccount))]
public OrderResult Execute(MyRequest order) {
}
The Interface looks like this:
[KnownType(typeof(CustomerIdByAccount))]
[KnownType(typeof(CustomerIdByName))]
public interface ICustomerId{
string GetId();
}
When I make a request using the SOAP end point, everything works just great. But when passing the request to the REST end point, I get the serialization error.
This is the request I'm using
<MyRequest>
<CustomerId>
<AccountId>59251</AccountId>
</CustomerId>
<IsRollback>false</IsRollback>
<OrderId>0</OrderId>
<OrderType>OrderSubscription</OrderType>
<PurchaseInfo>
<ObjectId>196521</ObjectId>
</PurchaseInfo>
<TransactionId>ABC123</TransactionGuid>
</MyRequest>
Since I had been stuck at this point for too long, I then changed the ICustomerId member to be an abstract class that implements ICustomerId. Again the SOAP end point works fine but sending the request to the rest end point I get an error that states "Cannot create abstract class"
What am I missing or doing wrong here?
Is this failing because the interface is nested in the complex type and not a direct parameter of the webservice method? I've used webservices that receive interfaces as parameter and with the KnownType decorators they work just fine. Same question applies to the abstract class, is this not working because the abstract class is nested within a member of the MyRequest complex type?
This is the error message I am getting:
Element CustomerId from namespace cannot have child contents to be deserialized as an object. Please use XmlNode[] to deserialize this pattern of XML
Have you tried decorating your interface as a RESTful method?
[ServiceContract]
public interface IMyRequest
{
[OperationContract]
[WebInvoke(
UriTemplate = "Requests/GetID",
Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped)]
string GetId(MyRequest myRequest);
...
Also:
make sure that the [DataMember] properties match your request payload. Everything that gets passed in your XML request payload must be caught by the serializer in the service. I recommend keeping the naming the same, but you can map it using [DataMember(name="MyProperty")]. Also, your [DataContract] must be mapped to match the name of the parent node of your XML payload like [DataContract(Name="MyRequest")] but only if the class is named differently than the xml node. Do this, and it will deserialize your xml into the server side object/dto
The error you're getting sounds like it's complaining about the complex type inside of your DataContract. Your complex type needs to be decorated for serialization the same as your MyRequest type.
Also ensure your REST endpoint is bound to webHttpBinding