SOLVED with this link
I'm developing a wcf service, with rest pattern. Now it is on a IIS server.
It has got a method callable with a post, which has to receive a xml data.
I'm trying to send this xml (with other parameter), but I'm getting only deserial errors:
in System.ServiceModel.Dispatcher.OperationFormatter.DeserializeRequest(Message message, Object[] parameters)
in System.ServiceModel.Dispatcher.DemultiplexingDispatchMessageFormatter.DeserializeRequest(Message message, Object[] parameters)
in System.ServiceModel.Dispatcher.UriTemplateDispatchFormatter.DeserializeRequest(Message message, Object[] parameters)
in System.ServiceModel.Dispatcher.DispatchOperationRuntime.DeserializeInputs(MessageRpc& rpc)
in System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
in System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
in System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
in System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
This is the request:
retista=0019&hashedStrig=dkfjdkfjd&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>
and this is the xml I'm trying to send:
<ArrayOfWrapClienti xmlns="http://schemas.datacontract.org/2004/07/aaa" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<WrapClienti>
<CODRETE>1111</CODRETE>
<CODICE>111</CODICE>
<NOME>AAA</NOME>
<INDIRIZZO>PIAZZA</INDIRIZZO>
<CITTA>AAA</CITTA>
<CAP>111</CAP>
<PROV>aa</PROV>
<CODICEFISCALE>111</CODICEFISCALE>
<PIVA>223611140236</PIVA>
<EMAIL/>
<ESPOSIZ_CONTABILE>1111</ESPOSIZ_CONTABILE>
<STATO>FALSE</STATO>
</WrapClienti>
</ArrayOfWrapClienti>
If you click here, you can find out how an xml is returned by my wcf. I thought I have to send the same xml with other data, but I get the deserial error.
Why?
How can I send the xml to my wcf? In this test phase, I'm using this.
If I can give to you other useful infos, ask me and I'll write them here.
UPDATE: The text of the error message says that there are invalid data at root level, row 1, pos 1. Also if I put
<?xml version="1.0" encoding="utf-8" ?>
at the root level.
Maybe are there some missing headers?
UPDATE server side:
this is how my server wcf is configured:
[OperationContract]
[WebGet(UriTemplate = "cliente.xml?retista={codret}&cliente={codiceCliente}&H={hashedString}", ResponseFormat = WebMessageFormat.Xml)]
List<WrapClienti> GetClienteXML(string codret, string codiceCliente, string hashedString);
and this is the called method:
public GenericResponse SetClientiXML(List<WrapClienti> clienti, string retista, string hashedString)
{
var api = new API();
return api.SetClienti(clienti, retista, hashedString);
}
as you can I see, I don't manage any xml/json deserialzing phase, it is managed by the wcf routine. For example, if I send a json list, it converts it to a List. This is what also with xml I want to see, but I'm getting the error at the top of this question.
ANOTHER UPDATE: I have to send 3 parameters in xml, so.... this is my last attempt, I think it is more correct than previouses. Now the server gives null reference error:
<?xml version="1.0" encoding="utf-8" ?>
<SetClientiXML
xmlns="http://tempuri.org/">
<XMLRequest>
<ArrayOfWrapClienti
xmlns="http://schemas.datacontract.org/2004/07/aaa"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<WrapClienti>
<CODRETE>111</CODRETE>
<CODICE>111</CODICE>
<NOME>aaa</NOME>
<INDIRIZZO>aaa</INDIRIZZO>
<CITTA>aaa</CITTA>
<CAP>aaa</CAP>
<PROV>aa</PROV>
<CODICEFISCALE>111</CODICEFISCALE>
<PIVA>2236401111236</PIVA>
<EMAIL/>
<ESPOSIZ_CONTABILE>111</ESPOSIZ_CONTABILE>
<STATO>FALSE</STATO>
</WrapClienti>
</ArrayOfWrapClienti>
<RETISTA>1111</RETISTA>
<HASHEDSTRING>oklkokokokok</HASHEDSTRING>
</XMLRequest>
</SetClientiXML>
I have also created this class:
[DataContract]
public class XMLRequest
{
[DataMember]
public List<WrapClienti> XML;
[DataMember]
public string RETISTA;
[DataMember]
public string HASHEDSTRING;
public XMLRequest() { }
public XMLRequest(List<WrapClienti> x, string r, string h)
{
this.HASHEDSTRING = h;
this.XML = x;
this.RETISTA = r;
}
}
and the portotype now is:
public GenericResponse SetClientiXML(XMLRequest xr)
Does this work?
string input =
"<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>";
input = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + input;
Here is code to serialize and de-serialize your xml
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication2
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
SetClientiXML client = new SetClientiXML()
{
xmlRequest = new XMLRequest()
{
arrayOfWrapClienti = new ArrayOfWrapClienti()
{
wrapClient = new List<WrapClient>() {
new WrapClient {
codrete = "0018",
codice = "20685",
nome = "A.T.E.R. Azienda Territoriale",
indirizzo = "PIAZZA POZZA",
citta = "Verona",
cap = "37123",
prov = "VR",
codiceFiscale = "00223640236",
piva = "223640236",
esposizContabile = "937,02",
stato = false
}
}
}
},
retista = "3303903",
hashedString = "oklkokokokok"
};
XmlSerializer serializer = new XmlSerializer(typeof(SetClientiXML));
StreamWriter writer = new StreamWriter(FILENAME);
serializer.Serialize(writer, client);
writer.Flush();
writer.Close();
writer.Dispose();
XmlSerializer xs = new XmlSerializer(typeof(SetClientiXML));
XmlTextReader reader = new XmlTextReader(FILENAME);
SetClientiXML newClient = (SetClientiXML)xs.Deserialize(reader);
}
}
[XmlRoot("SetClientiXML")]
public class SetClientiXML
{
[XmlElement("XMLRequest")]
public XMLRequest xmlRequest { get; set; }
[XmlElement("RETISTA")]
public string retista { get; set; }
[XmlElement("HASHEDSTRING")]
public string hashedString { get; set; }
}
[XmlRoot("XMLRequest")]
public class XMLRequest
{
[XmlElement("ArrayOfWrapClienti")]
public ArrayOfWrapClienti arrayOfWrapClienti { get; set; }
}
[XmlRoot("ArrayOfWrapClienti")]
public class ArrayOfWrapClienti
{
[XmlElement("WrapClient")]
public List<WrapClient> wrapClient { get; set; }
}
[XmlRoot("WrapClient")]
public class WrapClient
{
[XmlElement("CODRETE")]
public string codrete { get; set; }
[XmlElement("CODICE")]
public string codice { get; set; }
[XmlElement("NOME")]
public string nome { get; set; }
[XmlElement("INDIRIZZO")]
public string indirizzo { get; set; }
[XmlElement("CITTA")]
public string citta { get; set; }
[XmlElement("CAP")]
public string cap { get; set; }
[XmlElement("PROV")]
public string prov { get; set; }
[XmlElement("CODICEFISCALE")]
public string codiceFiscale { get; set; }
[XmlElement("PIVA")]
public string piva { get; set; }
[XmlElement("EMAIL")]
public string email { get; set; }
[XmlElement("ESPOSIZ_CONTABILE")]
public string esposizContabile { get; set; }
[XmlElement("STATO")]
public Boolean stato { get; set; }
}
}
Related
I see this question often enough, but nobody's title really seems to depict their question. I get a large response object back from a Web API that contains general response information, along with the data object I want to deserialize.
Full XML:
<?xml version="1.0"?>
<root>
<status>
<apiErrorCode>0</apiErrorCode>
<apiErrorMessage/>
<dbErrorCode>0</dbErrorCode>
<dbErrorMessage/>
<dbErrorList/>
</status>
<data>
<modelName>ReportXDTO</modelName>
<modelData>
<id>1780</id>
<reportTitle>Access Level (select) with Door Assignment</reportTitle>
<hasParameters>true</hasParameters>
<parameters>
<dataType>STRING</dataType>
<title>Access Level:</title>
<index>1</index>
<allowMulti>true</allowMulti>
<selectSql>SELECT DISTINCT [Name] FROM dbo.[Levels] WHERE [PrecisionFlag] = '0' ORDER BY [Name] </selectSql>
<values>
<value>Door 1</value>
<used>1</used>
</values>
<values>
<value>Door 2</value>
<used>1</used>
</values>
<values>
<value>Door 3</value>
<used>1</used>
</values>
</parameters>
<sourceSql>SELECT [Name], [SData] FROM [Schedules]</sourceSql>
<report/>
</modelData>
<itemReturned>1</itemReturned>
<itemTotal>1</itemTotal>
</data>
<listInfo>
<pageIdRequested>1</pageIdRequested>
<pageIdCurrent>1</pageIdCurrent>
<pageIdFirst>1</pageIdFirst>
<pageIdPrev>1</pageIdPrev>
<pageIdNext>1</pageIdNext>
<pageIdLast>1</pageIdLast>
<itemRequested>1</itemRequested>
<itemReturned>1</itemReturned>
<itemStart>1</itemStart>
<itemEnd>1</itemEnd>
<itemTotal>1</itemTotal>
</listInfo>
</root>
I only want to deserialize the modelData element. The modelData object type is dynamic, depending on the API call.
I deserialize xml in other applications, and created the following method, but don't know how to specifically ONLY get the modelData element:
public static T ConvertXmltoClass<T>(HttpResponseMessage http, string elementName) where T : new()
{
var newClass = new T();
try
{
var doc = JsonConvert.DeserializeXmlNode(http.Content.ReadAsStringAsync().Result, "root");
XmlReader reader = new XmlNodeReader(doc);
reader.ReadToFollowing(elementName);
//The xml needs to show the proper object name
var xml = reader.ReadOuterXml().Replace(elementName, newClass.GetType().Name);
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
{
var serializer = new XmlSerializer(typeof(T));
newClass = (T)serializer.Deserialize(stream);
}
}
catch (Exception e)
{
AppLog.LogException(System.Reflection.MethodBase.GetCurrentMethod().Name, e);
}
return newClass;
}
I have updated this thread multiple times now, to stay current. I started updating it with the first solution. But that solution by itself didn't solve the problem. With the code how it is right now, I get no exceptions, but don't get the xml deserialized to my object. Instead I get a new, blank object. Thoughts?
THOUGH the object type can change, here is my current object I am dealing with: (PLEASE NOTE, that I deserialize the exact xml in modelData, in the Web API)
namespace WebApiCommon.DataObjects
{
[Serializable]
public class ReportXDto
{
public ReportXDto()
{
Parameters = new List<ReportParameterXDto>();
}
public int Id { get; set; }
public string ReportTitle { get; set; }
public bool HasParameters { get; set; } = false;
public List<ReportParameterXDto> Parameters { get; set; }
public string SourceSql { get; set; }
public DataTable Report { get; set; }
}
[Serializable]
public class ReportXDto
{
public ReportXDto()
{
Parameters = new List<ReportParameterXDto>();
}
public int Id { get; set; }
public string ReportTitle { get; set; }
public bool HasParameters { get; set; } = false;
public List<ReportParameterXDto> Parameters { get; set; }
public string SourceSql { get; set; }
public DataTable Report { get; set; }
}
[Serializable]
public class ReportParameterValuesXDto
{
public string Value { get; set; } = "";
public bool Used { get; set; } = false;
}
}
Firstly, XmlSerializer is case sensitive. Thus your property names need to match the XML element names exactly -- unless overridden with an attribute that controls XML serialization such as [XmlElement(ElementName="id")]. To generate a data model with the correct casing I used http://xmltocsharp.azurewebsites.net/ which resulted in:
public class ReportParameterValuesXDto
{
[XmlElement(ElementName="value")]
public string Value { get; set; }
[XmlElement(ElementName="used")]
public string Used { get; set; }
}
public class ReportParametersXDto
{
[XmlElement(ElementName="dataType")]
public string DataType { get; set; }
[XmlElement(ElementName="title")]
public string Title { get; set; }
[XmlElement(ElementName="index")]
public string Index { get; set; }
[XmlElement(ElementName="allowMulti")]
public string AllowMulti { get; set; }
[XmlElement(ElementName="selectSql")]
public string SelectSql { get; set; }
[XmlElement(ElementName="values")]
public List<ReportParameterValuesXDto> Values { get; set; }
}
public class ReportXDto
{
[XmlElement(ElementName="id")]
public string Id { get; set; }
[XmlElement(ElementName="reportTitle")]
public string ReportTitle { get; set; }
[XmlElement(ElementName="hasParameters")]
public string HasParameters { get; set; }
[XmlElement(ElementName="parameters")]
public ReportParametersXDto Parameters { get; set; }
[XmlElement(ElementName="sourceSql")]
public string SourceSql { get; set; }
[XmlElement(ElementName="report")]
public string Report { get; set; }
}
(After generating the model, I modified the class names to match your naming convention.)
Given the correct data model, you can deserialize directly from a selected XmlNode using an XmlNodeReader as shown in How to deserialize a node in a large document using XmlSerializer without having to re-serialize to an intermediate XML string. The following extension method does the trick:
public static partial class XmlExtensions
{
public static IEnumerable<T> DeserializeElements<T>(this XmlNode root, string localName, string namespaceUri)
{
return new XmlNodeReader(root).DeserializeElements<T>(localName, namespaceUri);
}
public static IEnumerable<T> DeserializeElements<T>(this XmlReader reader, string localName, string namespaceUri)
{
var serializer = XmlSerializerFactory.Create(typeof(T), localName, namespaceUri);
while (!reader.EOF)
{
if (!(reader.NodeType == XmlNodeType.Element && reader.LocalName == localName && reader.NamespaceURI == namespaceUri))
reader.ReadToFollowing(localName, namespaceUri);
if (!reader.EOF)
{
yield return (T)serializer.Deserialize(reader);
// Note that the serializer will advance the reader past the end of the node
}
}
}
}
public static class XmlSerializerFactory
{
// To avoid a memory leak the serializer must be cached.
// https://stackoverflow.com/questions/23897145/memory-leak-using-streamreader-and-xmlserializer
// This factory taken from
// https://stackoverflow.com/questions/34128757/wrap-properties-with-cdata-section-xml-serialization-c-sharp/34138648#34138648
readonly static Dictionary<Tuple<Type, string, string>, XmlSerializer> cache;
readonly static object padlock;
static XmlSerializerFactory()
{
padlock = new object();
cache = new Dictionary<Tuple<Type, string, string>, XmlSerializer>();
}
public static XmlSerializer Create(Type serializedType, string rootName, string rootNamespace)
{
if (serializedType == null)
throw new ArgumentNullException();
if (rootName == null && rootNamespace == null)
return new XmlSerializer(serializedType);
lock (padlock)
{
XmlSerializer serializer;
var key = Tuple.Create(serializedType, rootName, rootNamespace);
if (!cache.TryGetValue(key, out serializer))
cache[key] = serializer = new XmlSerializer(serializedType, new XmlRootAttribute { ElementName = rootName, Namespace = rootNamespace });
return serializer;
}
}
}
Then you would deserialize as follows:
var modelData = doc.DeserializeElements<ReportXDto>("modelData", "").FirstOrDefault();
Working sample .Net fiddle here.
For Huge xml files always use XmlReader so you do not get an out of memory issue. See code below to get the element as a string :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
//or Create(Stream)
XmlReader reader = XmlReader.Create(FILENAME);
reader.ReadToFollowing("modelData");
if (!reader.EOF)
{
string modelDataStr = reader.ReadOuterXml();
}
}
}
}
This queston may possible duplicated but I am calling a service as the following below :
HttpClient httpClinet = new HttpClient();
httpClinet.DefaultRequestHeaders.Accept.Clear();
httpClinet.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml"));
var str = "XrayService.asmx/GetOrdData?" + string.Format("ordId={0}&code={1}", range.ordId, range.code);
HttpResponseMessage response;
httpClinet.BaseAddress = new Uri("http://172.16.203.27:6043/");
response = httpClinet.GetAsync(str).Result;
if (response.IsSuccessStatusCode)
var caseInfos = response.Content.ReadAsAsync<IEnumerable<PI>>().Result;//Here is exception
everything is going fine, but when I want to run ReadAsAsync I got the exception as below:
Error:System.AggregateException: One or more errors occurred. ---> System.AggregateException: One or more errors occurred. ---> System.Runtime.Serialization.SerializationException: Error in line 1 position 5. Expecting element 'ArrayOfPI' from namespace 'http://schemas.datacontract.org/2004/07/SATA_DTOs'.. Encountered 'Element' with name 'PI', namespace ''.
at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlReader reader)
at System.Net.Http.Formatting.XmlMediaTypeFormatter.ReadFromStream(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
at System.Net.Http.Formatting.XmlMediaTypeFormatter.ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
--- End of stack trace from previous location where exception was thrown ---
I am testing that service by Google Advanced Rest Client and see the result as :
Status : 200 OK
Response Header:
cache-control: private, max-age=0
content-length: 360
content-type: text/xml; charset=utf-8
server:Microsoft-IIS/8.5
x-aspnet-version:4.0.30319
x-powered-by: ASP.NET
date: Sun, 03 Dec 2017 08:37:21 GMT
and OutPut :
<?xml version="1.0" encoding="utf-8" ?>
<PI>
<ordId>950177248</ordId>
<fnm>بهسا</fnm>
<lnm>حسنی</lnm>
<fthNm>علی</fthNm>
<pId>p2535154</pId>
<sex>F</sex>
<brthD>2003-02-05</brthD>
<addrs />
<nId>0025351540</nId>
<srvNm>|دندان بصورت پانورک</srvNm>
<rfrPhy>مهرزاد اميري-41853</rfrPhy>
</PI>
I also decorated DTO like :
namespace SATA_DTOs
{
[DataContract(Name = "PI")]
public class PI
{
[DataMember] public string ordId { get; set; }
[DataMember] public string fnm { get; set; }
[DataMember] public string lnm { get; set; }
[DataMember] public string fthNm { get; set; }
[DataMember] public string pId { get; set; }
[DataMember] public string sex { get; set; }
[DataMember] public string brthD { get; set; }
[DataMember] public string addrs { get; set; }
[DataMember] public string nId { get; set; }
[DataMember] public string srvNm { get; set; }
[DataMember] public string rfrPhy { get; set; }
}
}
UPDATE:
Just as another try I want to get the result here either JSON or XML but this is also does not make difference:
List<PI> model = null;
var client = new HttpClient();
var task = client.GetAsync(httpClinet.BaseAddress.ToString() + str)
.ContinueWith((taskwithresponse) =>
{
var response1 = taskwithresponse.Result;
var jsonString = response1.Content.ReadAsStringAsync();
m_Logging.Log(SharedLib.LoggingMode.Prompt, "JSON string created {0}...", jsonString);
jsonString.Wait();
model = Newtonsoft.Json.JsonConvert.DeserializeObject<List<PI>>(jsonString.Result);
});
task.Wait();
After many copy pasting patch on server and improving the log I finally resolve the problem,
As the last try which #NKosi suggested with little changes:
var response1 = httpClinet.GetAsync(str).Result;
IEnumerable<PI> caseInfos1 = Enumerable.Empty<PI>();
try
{
caseInfos1 = response1.Content.ReadAsAsync<IEnumerable<PI>>().Result;
}
catch (Exception ex)
{
try
{
m_Logging.Log(SharedLib.LoggingMode.Error, "IEnumerable failed, EXP:{0}", ex);
var singleObject = response1.Content.ReadAsAsync<PI>().Result;
if (singleObject != null)
{
m_Logging.Log(SharedLib.LoggingMode.Error, "singleObject succeeded...");
caseInfos1 = new[] { singleObject };
}
}
catch (Exception exp)
{
m_Logging.Log(SharedLib.LoggingMode.Error, "singleObject failed, EXP:{0}", exp);
}
}
I crossed with the below exception also:
System.Runtime.Serialization.SerializationException: Error in line 1 position 5. Expecting element 'ArrayOfPI' from namespace 'http://schemas.datacontract.org/2004/07/SATA_DTOs'.. Encountered 'Element' with name 'PI', namespace ''....
.....
as the exception mentioned it can't be able to deserialize the result I guessed the out put type may is text/html not text/xml although Rest Client Tester specified it as text/xml,
for this reason I came to this conclusion to use ReadAsStringAsync and deserialize it to PI, so by the below snipped code I finally get the result:
PI caseInfos = null;
try
{
string strasd = response.Content.ReadAsStringAsync().Result;
m_Logging.Log(SharedLib.LoggingMode.Prompt, "ReadAsStringAsync() result:{0}", strasd);
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(PI));
using (TextReader reader = new StringReader(strasd))
caseInfos = (PI)serializer.Deserialize(reader);
m_Logging.Log(SharedLib.LoggingMode.Prompt, "Deserializing caseInfos model succeeded...");
}
catch (Exception ex)
{
m_Logging.Log(SharedLib.LoggingMode.Error, "creating model failed, EXP:{0}", ex);
}
I appreciate all crossed this question especially those who shared his/her knowledge in this discussion!!!
This is can be quite confusing. This might seem obvious the eye but still, not understandable to some.
Look at this line:
var caseInfos = response.Content.ReadAsAsync<IEnumerable<PI>>().Result
You are rendering the result as PI, which results in <pi> tag.
Thus, you get the following error:
System.Runtime.Serialization.SerializationException: Error in line 1
position 5. Expecting element 'ArrayOfPI' from namespace
'http://schemas.datacontract.org/2004/07/SATA_DTOs'.. Encountered
'Element' with name 'PI', namespace ''
The solution is this,
change your data contract and your class name:
namespace SATA_DTOs
{
[DataContract(Name = "ArrayOfPI")]
public class ArrayOfPI
{
[DataMember] public string ordId { get; set; }
[DataMember] public string fnm { get; set; }
[DataMember] public string lnm { get; set; }
[DataMember] public string fthNm { get; set; }
[DataMember] public string pId { get; set; }
[DataMember] public string sex { get; set; }
[DataMember] public string brthD { get; set; }
[DataMember] public string addrs { get; set; }
[DataMember] public string nId { get; set; }
[DataMember] public string srvNm { get; set; }
[DataMember] public string rfrPhy { get; set; }
}
}
Then assign it like this:
var caseInfos = response.Content.ReadAsAsync<IEnumerable<ArrayOfPI>>().Result
Thus, you will receive this:
<?xml version="1.0" encoding="utf-8" ?>
<ArrayOfPI>
<ordId>950177248</ordId>
<fnm>بهسا</fnm>
<lnm>حسنی</lnm>
<fthNm>علی</fthNm>
<pId>p2535154</pId>
<sex>F</sex>
<brthD>2003-02-05</brthD>
<addrs />
<nId>0025351540</nId>
<srvNm>|دندان بصورت پانورک</srvNm>
<rfrPhy>مهرزاد اميري-41853</rfrPhy>
</ArrayOfPI>
Which is what the serialization looks for and expects to find.
When using ReadAsAsync the framework tries to interpret the desired type for deserialization using the provided media type formatter.
You have IEnumerable<PI> so it assumes that the content being read is a collection ArrayOfPI based on standards.
In your example a single object is being returned while you tell it to expect a collection so it fails.
I suggest checking for collection and if that fails then check for single object given the dynamic nature of the responses that can be returned.
Simplified example
public async Task<IEnumerable<PI>> GetDataAsync() {
var httpClinet = buildClient();
var str = buildRequestUrl();
var response = await httpClinet.GetAsync(str);
IEnumerable<PI> caseInfos = Enumerable.Empty<PI>();
if (response.IsSuccessStatusCode) {
try {
caseInfos = await response.Content.ReadAsAsync<IEnumerable<PI>>();
} catch {
//Log?
}
if (caseInfos == null) {
try {
var singleObject = await response.Content.ReadAsAsync<PI>();
if (singleObject != null) {
caseInfos = new[] { singleObject };
}
} catch {
//Log?
}
}
}
return caseInfos;
}
Im developing a web api in C#. The web api should return an xml like:
<personDatas>
<personData>
<affdatalist>
<object1> information </object1>
<object2> information </object2>
<object3> information </object3>
</affdatalist>
<anotherObject1> infooo </anotherObject1>
<anotherObject2> infooo </anotherObject2>
</personData>
</personDatas>
The xml can have 1 to many personData elements and the personData element can have 1 to many affdatalist elements.
What would be the best practice to generate such an XML in a web api using C# 6?
Ive tried with XSD based on a schema definition.
Any help would be greatly appreciated.
You can use the Serialize method to generate this xml from the model it self.
public string SerializeXml<T>(T config)
{
XmlSerializer xsSubmit = new XmlSerializer(typeof(T));
string xml = "";
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = false;
using (var sww = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sww, settings))
{
xsSubmit.Serialize(writer, config);
xml = sww.ToString();
}
}
return xml;
}
This will return XML string , you need the model which is similar to your required XML.
This is what i have created as model from your XML that you can change as per your requiment.
[XmlRoot(ElementName = "affdatalist")]
public class Affdatalist
{
[XmlElement(ElementName = "object1")]
public string Object1 { get; set; }
[XmlElement(ElementName = "object2")]
public string Object2 { get; set; }
[XmlElement(ElementName = "object3")]
public string Object3 { get; set; }
}
[XmlRoot(ElementName = "personData")]
public class PersonData
{
[XmlElement(ElementName = "affdatalist")]
public Affdatalist Affdatalist { get; set; }
[XmlElement(ElementName = "anotherObject1")]
public string AnotherObject1 { get; set; }
[XmlElement(ElementName = "anotherObject2")]
public string AnotherObject2 { get; set; }
}
[XmlRoot(ElementName = "personDatas")]
public class PersonDatas
{
[XmlElement(ElementName = "personData")]
public PersonData PersonData { get; set; }
}
You can use as sample below
PersonDatas data = new PersonDatas();
var xml = this.SerializeXml<PersonDatas>(data); // your model with data
This is my sample after using what Sulay Shah said:
PersonDatas data = new PersonDatas();
for (int x = 0; x < 2; x++)
{
data.PersonData = new PersonData();
for (int i = 0; i < 2; i++)
{
Affdatalist affdata = new Affdatalist();
affdata.Object1 = "LALALALALLALA";
affdata.Object2 = "lqlqlqlqlqlqlql";
affdata.Object3 = "ililililililililililil";
data.PersonData.Affdatalist.Add(affdata);
}
data.PersonData.AnotherObject1 = "ali";
data.PersonData.AnotherObject2 = "Nazar";
data.personDataList.Add(data.PersonData);
}
var xml = this.SerializeXml<PersonDatas>(data);
return xml;
The above generated below
xml:<?xml version="1.0" encoding="utf-16"?>
<personDatas xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<personData>
<affdatalist>
<object1>LALALALALLALA</object1>
<object2>lqlqlqlqlqlqlql</object2>
<object3>ililililililililililil</object3>
</affdatalist>
<affdatalist>
<object1>LALALALALLALA</object1>
<object2>lqlqlqlqlqlqlql</object2>
<object3>ililililililililililil</object3>
</affdatalist>
<anotherObject1>ali</anotherObject1>
<anotherObject2>Nazar</anotherObject2>
</personData>
<personData>
<affdatalist>
<object1>LALALALALLALA</object1>
<object2>lqlqlqlqlqlqlql</object2>
<object3>ililililililililililil</object3>
</affdatalist>
<affdatalist>
<object1>LALALALALLALA</object1>
<object2>lqlqlqlqlqlqlql</object2>
<object3>ililililililililililil</object3>
</affdatalist>
<anotherObject1>ali</anotherObject1>
<anotherObject2>Nazar</anotherObject2>
</personData>
<PersonData>
<affdatalist>
<object1>LALALALALLALA</object1>
<object2>lqlqlqlqlqlqlql</object2>
<object3>ililililililililililil</object3>
</affdatalist>
<affdatalist>
<object1>LALALALALLALA</object1>
<object2>lqlqlqlqlqlqlql</object2>
<object3>ililililililililililil</object3>
</affdatalist>
<anotherObject1>ali</anotherObject1>
<anotherObject2>Nazar</anotherObject2>
</PersonData>
</personDatas>
If you want c# classes like this
using System;
using System.Xml.Serialization;
using System.Collections.Generic;
namespace namespace
{
public class AffData {
public string Property1 { get; set; }
}
public class AnotherObject1 {
public string Property1 { get; set; }
}
public class AnotherObject2 {
public string Property1 { get; set; }
}
public class PersonData {
public List<AffData> AffDataList { get; set; }
public AnotherObject1 AnotherObject1 { get; set; }
public AnotherObject2 AnotherObject2 { get; set; }
}
The below xml will work if you serialize List
<ArrayOfPersonData>
<PersonData>
<ArrayOfAffData>
<AffData>
<Property1>info</Property1>
</AffData>
<AffData> <Property1>info</Property1></AffData>
<AffData> <Property1>info</Property1> </AffData>
</ArrayOfAffData>
<anotherObject1> <Property1>info</Property1> </anotherObject1>
<anotherObject2> <Property1>info</Property1> </anotherObject2>
</PersonData>
</ArrayOfPersonData>
public static string ConvertToXml(object payload, string rootElement, bool addPrefixSuffix)
{
string json = JsonConvert.SerializeObject(payload);
string elementName = "DummyRoot";
if (addPrefixSuffix)
{
string prefix = "{" + elementName + ":";
string postfix = "}";
json = string.Concat(prefix, json, postfix);
}
var payloadXml = JsonConvert.DeserializeXNode(json, rootElement)?.ToString();
if (addPrefixSuffix)
{
payloadXml = payloadXml.ToString().Replace("<" + elementName + ">", string.Empty);
payloadXml = payloadXml.ToString().Replace("</" + elementName + ">", string.Empty);
}
return Regex.Replace(payloadXml, #"\s+", String.Empty);
}
Use the above function like ConvertToXml(requeststring, "", false)... Here second parameter empty will returns your expected result.
I have a WPF application that calls an API and creates an System.Xml.Linq.XDocument using XDocument.Parse(string). I am running into a problem where an XmlException ("Root element is missing") is thrown when I try to do this, but my XML is completely valid. I tried syntax-checking it by calling the API in my browser and checking its syntax, calling the API in my application and Console.WriteLineing the response, and using various XML syntax validators (all of which returned no errors).
A sample XML response from the API is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<event title="Event 1" id="75823347" icon="www.example.com/images/event1-icon.png" uri="www.example.com/rsvp/event1" mode="none" price="10.00" cover="www.example.com/event1-cover.png" enddate="2016-06-01 14:00:00" startdate="2016-06-01 12:00:00" address="1 Example St, Example City State 12345" location="Example Place" description="This is an event" shortdescription="This is an event" theme="auto" color="#FF000000"/>
</response>
This is my application's code:
public static WebRequest CreateRequest(string baseUrl, string httpMethod, Dictionary<string, string> requestValues) {
var requestItems = requestValues == null ? null : requestValues.Select(pair => string.Format("&{0}={1}", pair.Key, pair.Value));
var requestString = "";
if (requestItems != null)
foreach (var s in requestItems)
requestString += s;
var request = WebRequest.CreateHttp(baseUrl + CredentialRequestString + requestString);
request.Method = httpMethod.ToUpper();
request.ContentType = "application/x-www-form-urlencoded";
request.Credentials = CredentialCache.DefaultCredentials;
return request;
}
public static WebRequest CreateRequest(string apiEndpoint, string endpointParam, int apiVersion, string httpMethod, Dictionary<string, string> requestValues) {
return CreateRequest(string.Format("http://www.example.com/api/v{0}/{1}/{2}", apiVersion, apiEndpoint, endpointParam), httpMethod, requestValues);
}
public static async Task<string> GetResponseFromServer(WebRequest request) {
string s;
using (var response = await request.GetResponseAsync()) {
using (var responseStream = response.GetResponseStream()) {
using (var streamReader = new StreamReader(responseStream)) {
s = streamReader.ReadToEnd();
}
}
}
return s;
}
public static async Task<List<Event>> GetEvents() {
var response = await GetResponseFromServer(CreateRequest("events", "", 1, "GET", null));
Console.WriteLine(response); //validation
var data = XDocument.Parse(response).Root; //XmlException: Root element is mising
return new List<Event>(data.Elements("event").Select(e => Event.FromXml(e.Value)));
}
Why is this happening?
The following code works
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
var data = XDocument.Load(FILENAME);
Event _event = Event.FromXml(data.Descendants("event").FirstOrDefault());
}
}
public class Event
{
public string title { get ; set; }
public string icon {get; set; }
public string uri { get; set; }
public string mode { get;set; }
public decimal price { get; set; }
public string cover { get; set; }
public DateTime enddate { get; set; }
public DateTime startdate { get; set; }
public string address { get; set; }
public string location { get; set; }
public string description { get; set; }
public string shortdescription { get; set; }
public string theme { get; set; }
public uint color { get; set; }
public static Event FromXml(XElement data)
{
Event _event = new Event();
_event.title = (string)data.Attribute("title");
_event.icon = (string)data.Attribute("icon");
_event.uri = (string)data.Attribute("uri");
_event.mode = (string)data.Attribute("mode");
_event.price = (decimal)data.Attribute("price");
_event.cover = (string)data.Attribute("cover");
_event.enddate = (DateTime)data.Attribute("enddate");
_event.startdate = (DateTime)data.Attribute("startdate");
_event.address = (string)data.Attribute("address");
_event.location = (string)data.Attribute("location");
_event.description = (string)data.Attribute("description");
_event.shortdescription = (string)data.Attribute("shortdescription");
_event.theme = (string)data.Attribute("theme");
_event.color = uint.Parse(data.Attribute("color").Value.Substring(1), System.Globalization.NumberStyles.HexNumber) ;
return _event;
}
}
}
I am looking to deserialize data and place it into a generic class from a response from Azure.
<ServiceResources xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/windowsazure">
<ServiceResource>
<Name>Airport1</Name>
<Type>Microsoft.SqlAzure.FirewallRule</Type>
<State>Normal</State>
<SelfLink>https://management.core.windows.net:xxx/xxx/services/sqlservers/servers/xxx/firewallrules/Airport1</SelfLink>
<ParentLink>https://management.core.windows.net:xxxx/services/sqlservers/servers/xxx</ParentLink>
<StartIPAddress>000.000.000.000</StartIPAddress>
<EndIPAddress>2000.000.000.000</EndIPAddress>
</ServiceResource>
There are several objects I need to deserialze into my class.
[Serializable, XmlRoot(ElementName = "ServiceResource", Namespace = "http://schemas.microsoft.com/windowsazure/")]
public class ServiceResource
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Type")]
public string Type { get; set; }
[XmlElement("State")]
public string State { get; set; }
[XmlElement("SelfLink")]
public string SelfLink { get; set; }
[XmlElement("ParentLink")]
public string ParentLink { get; set; }
[XmlElement("StartIPAddress")]
public string StartIPAddress { get; set; }
[XmlElement("EndIPAddress")]
public string EndIPAddress { get; set; }
}
I have tried several different ventures into this and can't nail it. I have used the xmlSerializer but hit blocks on that.
using (var responseStreamReader = new StreamReader(webResponse.GetResponseStream()))
{
XmlSerializer serializer = new XmlSerializer(typeof(ServiceResource));
ServiceResource deserialized = (ServiceResource)serializer.Deserialize(responseStreamReader);
}
Any help would be gratefully accepted.
Answer
The Azure REST Api is returning a list of ServiceResource in the XML. So you need to encapsulate that into a class. Here is an example.
[XmlRoot(
ElementName = "ServiceResources",
Namespace = "http://schemas.microsoft.com/windowsazure")]
public class ServiceResources
{
public ServiceResources()
{
Items = new List<ServiceResource>();
}
[XmlElement("ServiceResource")]
public List<ServiceResource> Items { get; set; }
}
public class ServiceResource
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Type")]
public string Type { get; set; }
[XmlElement("State")]
public string State { get; set; }
[XmlElement("SelfLink")]
public string SelfLink { get; set; }
[XmlElement("ParentLink")]
public string ParentLink { get; set; }
[XmlElement("StartIPAddress")]
public string StartIPAddress { get; set; }
[XmlElement("EndIPAddress")]
public string EndIPAddress { get; set; }
}
With those two classes, you can now do the following.
var response = request.GetResponse();
var message = string.Empty;
using (var responseStreamReader = new StreamReader(response.GetResponseStream()))
{
message = responseStreamReader.ReadToEnd();
}
var textReader = new StringReader(message);
var serializer = new XmlSerializer(typeof(ServiceResources));
var serviceResources =
serializer.Deserialize(textReader) as ServiceResources;
Demo Console App
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace DeserializeAzureXmlResponse
{
class Program
{
private static string certificateThumbprint = "19DAED4D4ABBE0D400DC33A6D99D00D7BBB24472";
private static string subscriptionId = "14929cfc-3501-48cf-a5c9-b24a7daaf694";
static string sqlServerName = "mvp2015";
static string managementUri = "https://management.core.windows.net";
static string sqlServerApi = "services/sqlservers/servers";
static string firewallRules = "firewallrules";
static void Main(string[] args)
{
var restUri = CreateRestUri();
var clientCert = GetX509FromPersonalStore();
var request = (HttpWebRequest)HttpWebRequest.Create(restUri);
request.Headers.Add("x-ms-version", "2012-03-01");
request.ClientCertificates.Add(clientCert);
var response = request.GetResponse();
var message = string.Empty;
using (var responseStreamReader = new StreamReader(response.GetResponseStream()))
{
message = responseStreamReader.ReadToEnd();
}
var textReader = new StringReader(message);
var serializer = new XmlSerializer(typeof(ServiceResources));
var serviceResources = serializer.Deserialize(textReader) as ServiceResources;
foreach (var sr in serviceResources.Items)
{
Console.WriteLine("Name".PadRight(20) + sr.Name);
Console.WriteLine("Type".PadRight(20) + sr.Type);
Console.WriteLine("State".PadRight(20) + sr.State);
Console.WriteLine("SelfLink".PadRight(20) + sr.SelfLink);
Console.WriteLine("ParentLink".PadRight(20) + sr.ParentLink);
Console.WriteLine("StartIP".PadRight(20) + sr.StartIPAddress);
Console.WriteLine("EndIP".PadRight(20) + sr.EndIPAddress);
Console.WriteLine("+++++++++++");
}
Console.ReadLine();
}
static Uri CreateRestUri()
{
// https://management.core.windows.net/{subscriptionID}/services/sqlservers/servers/{server}/firewallrules/
var builder = new StringBuilder();
builder.Append(managementUri + "/");
builder.Append(subscriptionId + "/");
builder.Append(sqlServerApi + "/");
builder.Append(sqlServerName + "/");
builder.Append(firewallRules + "/");
var uri = new Uri(builder.ToString());
return uri;
}
static X509Certificate GetX509FromPersonalStore()
{
// To view the personal store, press `Win + R` and then type `certmgr.msc`
var store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, certificateThumbprint, true);
var certificate = certificates[0];
store.Close();
return certificate;
}
}
[XmlRoot(
ElementName = "ServiceResources",
Namespace = "http://schemas.microsoft.com/windowsazure")]
public class ServiceResources
{
public ServiceResources()
{
Items = new List<ServiceResource>();
}
[XmlElement("ServiceResource")]
public List<ServiceResource> Items { get; set; }
}
public class ServiceResource
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Type")]
public string Type { get; set; }
[XmlElement("State")]
public string State { get; set; }
[XmlElement("SelfLink")]
public string SelfLink { get; set; }
[XmlElement("ParentLink")]
public string ParentLink { get; set; }
[XmlElement("StartIPAddress")]
public string StartIPAddress { get; set; }
[XmlElement("EndIPAddress")]
public string EndIPAddress { get; set; }
}
}
Output
Name My-House
Type Microsoft.SqlAzure.FirewallRule
State Normal
SelfLink https://management.core.windows.net/14929cfc-35
ParentLink https://management.core.windows.net/14929cfc-35
StartIP 123.435.234.643
EndIP 123.435.234.643
+++++++++++
Name AllowAllWindowsAzureIps
Type Microsoft.SqlAzure.FirewallRule
State Normal
SelfLink https://management.core.windows.net/14929cfc-35
ParentLink https://management.core.windows.net/14929cfc-35
StartIP 0.0.0.0
EndIP 0.0.0.0
+++++++++++
See Also
Is it possible to deserialize XML into List<T>?
List Firewall Rules
I am assuming you are trying to deserialize the whole object graph. Given xml has root node ServiceResources which contains ServiceResource. You have two options, you can mimic the whole xml into classes and desrialize; or just get the inner node of ServiceResource and deserialize that part.
If you use first option, then you would need to store ServiceResource inside another class which has mapped collections property with XmlElement name set to "ServiceResource", e.g.:
[XmlType(Namespace="http://schemas.microsoft.com/windowsazure")]
[XmlRoot(Namespace="http://schemas.microsoft.com/windowsazure")]
public class ServiceResource
{
public string Name { get; set; }
public string Type { get; set; }
public string State { get; set; }
public string SelfLink { get; set; }
public string ParentLink { get; set; }
public string StartIPAddress { get; set; }
public string EndIPAddress { get; set; }
}
[XmlType(Namespace="http://schemas.microsoft.com/windowsazure")]
[XmlRoot(Namespace="http://schemas.microsoft.com/windowsazure")]
public class ServiceResources
{
[XmlElement("ServiceResource")]
public List<ServiceResource> ServiceResource { get; set; }
}
With that you should be able to deserialize directly. Container class has the collections mapped to the ServiceResource element, which will load all of the nodes for service resource. Note: deserialization target type is now "ServiceResources" not the inner type "ServiceResource"
using (var responseStreamReader = new StreamReader(webResponse.GetResponseStream()))
{
XmlSerializer serializer = new XmlSerializer(typeof(ServiceResources));
ServiceResources deserialized = (ServiceResources)serializer.Deserialize(responseStreamReader);
//you can access each item in loop
foreach(var res in deserialized.ServiceResource)
{
//access items e.g.
var name = res.Name;
}
}