De-Serialization of a nested object tree using WCF DataContractSerializer - c#

I have the following SOAP XML Response
<startProcessResponseDDWEBCall xmlns="http://eclipse.org/stardust/models/generated/OmniLinkServices">
<ProcessInstanceOid>13430</ProcessInstanceOid>
<Return>
<DDWEBCallResponseData>
<DDWEBCallOutput xmlns="http://www.infinity.com/bpm/model/OmniLinkServices/DDWEBCallOutput">
<CommonResponse xmlns=""/>
<reportContent xmlns=""><![CDATA[<HTML><BODY><P>The OmniPlus Host Server process DDWEB had the following error: </P><P><TEXT="FF000">E23 TX00087 Textfile not found: TestScript # 000003\\\ </TEXT></P><P>processing terminated</P></BODY></HTML>]]></reportContent>
</DDWEBCallOutput>
</DDWEBCallResponseData>
</Return>
</startProcessResponseDDWEBCall>
This is the SOAP body of a WCF web service call response. I have the following object hierarchy to represent the response
[DataContract(Namespace="http://www.infinity.com/bpm/model/OmniLinkServices/DDWEBCallOutput")]
public class OmniLinkExecuteScriptOutput
{
public string CommonResponse { get; set; }
[DataMember(Name = "reportContent")]
public string ReportContent { get; set; }
}
[DataContract(Namespace = "http://eclipse.org/stardust/models/generated/OmniLinkServices")]
public class OmniLinkExecuteScriptResponseData
{
[DataMember(Name="DDWEBCallOutput")]
public OmniLinkExecuteScriptOutput Output { get; set; }
}
[DataContract(Namespace = "http://eclipse.org/stardust/models/generated/OmniLinkServices")]
public class OmniLinkExecuteScriptReturn
{
[DataMember(Name="DDWEBCallResponseData")]
public OmniLinkExecuteScriptResponseData ReponseData { get; set; }
}
[MessageContract(WrapperName = "startProcessResponseDDWEBCall", WrapperNamespace = "http://eclipse.org/stardust/models/generated/OmniLinkServices", IsWrapped = true)]
public class OmniLinkExecuteScriptResponse
{
[MessageBodyMember(Name = "ProcessInstanceOid", Namespace = "http://eclipse.org/stardust/models/generated/OmniLinkServices")]
public string ProcessInstanceOid { get; set; }
[MessageBodyMember(Name = "Return", Namespace = "http://eclipse.org/stardust/models/generated/OmniLinkServices")]
public OmniLinkExecuteScriptReturn Return { get; set; }
}
The object OmniLinkExecuteScriptResponse is the return type of a method call. Everything is deserialized fine except for the inner most object, the object representing the DDWEBCallOutput node. I don't see any errors in the WCF plumbing, and the value of the Output property on the OmniLinkExecuteScriptResponseData object is always null.
Can anybody tell what I'm doing wrong?

Your problem is that you are applying the [DataContract(Namespace = "...")] attributes at the wrong level in your class hierarchy. This attribute controls the namespace to which all data members of an instance of a type are serialized, and in addition controls the root element namespace if the instance is being serialized as such.
Thus you need to do the following:
// Its members are in the empty namespace
[DataContract(Namespace = "")]
public class OmniLinkExecuteScriptOutput
{
[DataMember]
public string CommonResponse { get; set; }
[DataMember(Name="reportContent")]
public string ReportContent { get; set; }
}
// Its members are in the DDWEBCallOutput namespace
[DataContract(Namespace = "http://www.infinity.com/bpm/model/OmniLinkServices/DDWEBCallOutput")]
public class OmniLinkExecuteScriptResponseData
{
[DataMember(Name = "DDWEBCallOutput")]
public OmniLinkExecuteScriptOutput Output { get; set; }
}
// Its members are in the OmniLinkServices namespace
[DataContract(Namespace = "http://eclipse.org/stardust/models/generated/OmniLinkServices")]
public class OmniLinkExecuteScriptReturn
{
[DataMember(Name = "DDWEBCallResponseData")]
public OmniLinkExecuteScriptResponseData ReponseData { get; set; }
}
OmniLinkExecuteScriptResponse can remain unchanged.

Related

WCF Generic result class always is empty

I have this generic class for my server responses:
[DataContract]
public class GenericResult<T>
{
public List<T> ListResult { get; set; }
public T Result { get; set; }
public string Message { get; set; }
}
and this GetAllBrandsTest method to return data to the client:
public async Task<GenericResult<Brand>> GetAllBrandsTest()
{
var result = await repo.GetAllAsync<Brand>();
return new GenericResult<Brand>()
{
ListResult = result.ToList(),
Message = "Success"
};
}
Everything is OK with this methods counterpart GetAllBrands:
public async Task<IList<Brand>> GetAllBrands()
{
return await repo.GetAllAsync<Brand>();
}
But when I call GetAllBrandsTest the result is empty.
[DataContract]
[KnownType(typeof(Brand))]
public class GenericResult<T>
{
[DataMember]
public List<Brand> ListResult { get; set; }
[DataMember]
public Brand Result { get; set; }
[DataMember]
public string Message { get; set; }
}
Any data type transferred between the server-side and the client-side should be explicitly specified how we serialize and deserialize it. Please use the DataContract attribute to specify the data structure the way how to serialize to XML so that the serialization and deserialization can work properly between the service-side and client-side. In addition, for unknown data types, please use the KnownType feature to specify the serialization method in advance.
[DataContract]
[KnownType(typeof(CircleType))]
[KnownType(typeof(TriangleType))]
public class CompanyLogo2
[DataMember]
private Shape ShapeOfLogo;
[DataMember]
private int ColorOfLogo;
}
[DataContract]
public class Shape { }
[DataContract(Name = "Circle")]
public class CircleType : Shape { }
[DataContract(Name = "Triangle")]
public class TriangleType : Shape { }
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/using-data-contracts
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/data-contract-known-types
We either decorate the class with both DataContract attribute and DataMember attribute or remove the DataContract attribute and DataMember attribute. Because the DataContract serializer will be used by default when the complex data type is without specifying any XML serializer.

XML Deserialization error: xxxxx was not expected

I know there are several posts out there with this topic, but I can't seem to figure out what is the problem here. I have serialized and deserialized xml several times, and never had this error.
The exception message is:
There is an error in XML document (1, 2).
With InnerException:
<InvoiceChangeRequest xmlns=''> was not expected.
XML file I want to deserialize:
<ns1:InvoiceChangeRequest xmlns:ns1="http://kmd.dk/fie/external_invoiceDistribution">
<CONTROL_FIELDS>
<STRUCTURID>0000000001</STRUCTURID>
<OPERA>GET</OPERA>
<WIID>000050371220</WIID>
</CONTROL_FIELDS>
<HEADER_IN>
<MANDT>751</MANDT>
<BELNR>1234567890</BELNR>
</HEADER_IN>
<ITEMS>
<ITEM_FIELDS_IN>
<BUZEI>001</BUZEI>
<BUKRS>0020</BUKRS>
</ITEM_FIELDS_IN>
</ITEMS>
</ns1:InvoiceChangeRequest>
Class I'm trying to deserialize to:
[XmlRoot(Namespace = "http://kmd.dk/fie/external_invoiceDistribution", IsNullable = false)]
public class InvoiceChangeRequest
{
[XmlElement("CONTROL_FIELDS")] public ControlFields Styrefelter;
[XmlElement("HEADER_IN")] public HeaderIn HeaderfelterInd;
[XmlElement("ITEMS")] public Items Linjer;
}
public class HeaderIn
{
[XmlElement("MANDT")] public string Kommunenummer;
[XmlElement("BELNR")] public string RegnskabsbilagsNummer;
}
public class Items
{
[XmlElement("ITEM_FIELDS_IN")] public Itemfield[] ItemfelterInd;
}
public class Itemfield
{
[XmlElement("BUZEI")] public string Linjenummer;
[XmlElement("BUKRS")] public string Firmakode;
}
Deserialization code:
XmlSerializer serializer = new XmlSerializer(typeof(InvoiceChangeRequest));
var request = serializer.Deserialize(new StringReader(output)) as InvoiceChangeRequest;
In your XML file your root element is the namespace http://kmd.dk/fie/external_invoiceDistribution with prefix ns1.
The element <CONTROL_FIELDS> isn't because it isn't prefixed. Your serialization class doesn't take this into account though. That means that it expects that <CONTROL_FIELDS> and the other elements are ALSO in the ns1 namespace.
To get the serializer parse the elements correctly add the Namespace to the elements, setting it to an empty string:
[XmlRoot(Namespace = "http://kmd.dk/fie/external_invoiceDistribution", IsNullable = false)]
public class InvoiceChangeRequest
{
[XmlElement("CONTROL_FIELDS", Namespace = "")]
public ControlFields Styrefelter { get; set; }
[XmlElement("HEADER_IN", Namespace = "")]
public HeaderIn HeaderfelterInd { get; set; }
[XmlElement("ITEMS", Namespace = "")]
public Items Linjer { get; set; }
}
This will de-serialize the given XML as intended.
In case of de-serialization issues I often create the classes in memory and then serialize that so I can inspect the resulting XML. That often gives clues on what is missing or being added compared to the input document:
var ms = new MemoryStream();
serializer.Serialize(ms, new InvoiceChangeRequest {
Styrefelter = new ControlFields { Opera="test"}
});
var s = Encoding.UTF8.GetString(ms.ToArray());
And then inspect s for differences.
You can replace 'ns1:' with string.Empty.
Below classes should serialize.
public class Item
{
[XmlElement("BUZEI")]
public string Buzei { get; set; }
[XmlElement("BUKRS")]
public string Bukrs { get; set; }
}
public class Header
{
[XmlElement("MANDT")]
public string Mandt { get; set; }
[XmlElement("BELNR")]
public string Belnr { get; set; }
}
public class ControlFields
{
[XmlElement("STRUCTURID")]
public string StructuredId { get; set; }
[XmlElement("OPERA")]
public string Opera { get; set; }
[XmlElement("WIID")]
public string Wild { get; set; }
}
public class InvoiceChangeRequest
{
[XmlElement("CONTROL_FIELDS")]
public ControlFields ControlFields { get; set; }
[XmlElement("HEADER_IN")]
public Header Header { get; set; }
[XmlArray("ITEMS")]
[XmlArrayItem("ITEM_FIELDS_IN")]
public List<Item> Items { get; set; }
}

How to add xsi:schemaLocation to serialized object

How can I add the following xsi:schemaLocation to a serialized class?
<ern:NewReleaseMessage xmlns:ern="http://ddex.net/xml/2010/ern-main/32"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
LanguageAndScriptCode="en"
xsi:schemaLocation="http://ddex.net/xml/2010/ern-main/32 http://ddex.net/xml/2010/ern-main/32/ern-main.xsd"
MessageSchemaVersionId="2010/ern-main/32">
Here is what I have done so far:
public class NewReleaseMessage
{
[XmlAttribute]
public string LanguageAndScriptCode { get; set; }
[XmlAttribute("schemaLocation", Namespace = "http://ddex.net/xml/2010/ern-main/32")]
public string schemaLocation = "http://ddex.net/xml/2010/ern-main/32 http://ddex.net/xml/2010/ern-main/32/ern-main.xsd";
[XmlAttribute]
public string MessageSchemaVersionId { get; set; }
[XmlElement()]
public MessageHeader MessageHeader { get; set; }
}
When I deserialize the xml to the object in VS I get:
{"The method or operation is not implemented."
There is an error in XML document (5, 44) - This actually points to the line: xsi:schemaLocation="http://ddex.net/xml/2010/ern-main/32 http://ddex.net/xml/2010/ern-main/32/ern-main.xsd"
Soultion:
[XmlAttribute(AttributeName = "schemaLocation", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string schemaLocation { get; set; }
Below is what worked for me:
Note : "XmlAttributeAttribute"
[XmlAttributeAttribute(AttributeName = "schemaLocation", Namespace = "http://ddex.net/xml/2010/ern-main/32")]
public string schemaLocation = "http://ddex.net/xml/2010/ern-main/32 http://ddex.net/xml/2010/ern-main/32/ern-main.xsd";

C# XML Serializable Collection

I got the below XML (It is just a part of a big XML where I have my problem) that I am trying to make a Serializable class to read the same.
<BANKTRANLIST>
<DTSTART>20051001</DTSTART>
<DTEND>20051028</DTEND>
<STMTTRN> <!-- This element can repeat any number of times -->
<TRNTYPE>CHECK</TRNTYPE>
<DTPOSTED>20051004</DTPOSTED>
<TRNAMT>-200.00</TRNAMT>
</STMTTRN>
<STMTTRN>
<TRNTYPE>ATM</TRNTYPE>
<DTPOSTED>20051020</DTPOSTED>
<TRNAMT>-300.00</TRNAMT>
</STMTTRN>
</BANKTRANLIST>
My C# Implementation
[Serializable]
[XmlRoot("BANKTRANLIST", Namespace = "http://bank.net", IsNullable = false)]
public class BankTransactionList
{
public BankTransactionList()
{
this.StatementTransactions = new List<StatementTransaction>();
}
[XmlElement("DTSTART")]
public string StartDate { get; set; }
[XmlElement("DTEND")]
public string EndDate { get; set; }
[XmlArray("STMTTRN")]
[XmlArrayItem("STMTTRN")]
public List<StatementTransaction> StatementTransactions { get; set; }
}
[Serializable]
[XmlRoot("STMTTRN", Namespace = "http://bank.net", IsNullable = false)]
public class StatementTransaction
{
// TransactionType : ENUM
[XmlElement("TRNTYPE")]
public TransactionType TransactionType { get; set; }
[XmlElement("DTPOSTED")]
public string DatePosted { get; set; }
[XmlElement("TRNAMT")]
public double TransactionAmount { get; set; }
}
My problem is element wrapped again in element which results to get the below output
...
<STMTTRN> <!-- This does not match my Original XML -->
<STMTTRN>
<TRNTYPE>CHECK</TRNTYPE>
<DTPOSTED>20051004</DTPOSTED>
<TRNAMT>-200.00</TRNAMT>
</STMTTRN>
<STMTTRN>
<TRNTYPE>ATM</TRNTYPE>
<DTPOSTED>20051020</DTPOSTED>
<TRNAMT>-300.00</TRNAMT>
</STMTTRN>
</STMTTRN>
Note: Removing [XmlArray("STMTTRN")] tag from List property will not resolve this, instead it will be
If any one can correct me or give me a better solution would be great !!
Should be [XmlElement] if you want an element per item without a wrapper element:
[XmlElement("STMTTRN")]
public List<StatementTransaction> StatementTransactions { get; set; }
I would add that in order to serialize the collection you will need to have it something like this:
[Serializable]
[XmlRoot("BANKTRANLIST", Namespace = "http://bank.net", IsNullable = false)]
public class BankTransactionList
{
public BankTransactionList()
{
StatementTransactions = new List<StatementTransaction>()
{
new StatementTransaction()
};
}
[XmlElement("DTSTART")]
public string StartDate { get; set; }
[XmlElement("DTEND")]
public string EndDate { get; set; }
[XmlElement("STMTTRN")]
public List<StatementTransaction> StatementTransactions { get; set; }
}
Otherwise, without initializing the StatementTransaction object, the list will not be serialized.

Unexpected tag names in WCF Service Response

I have a wcf service and I have class like below:
public class Message
{
[XmlElement(ElementName = "message")]
[DataMember(Name = "message")]
public string message { get; set; }
[XmlElement(ElementName = "MsgID")]
[DataMember(Name = "MsgID")]
public string MsgID{ get; set; }
}
Then i browsed my service in a browser the XML i am getting like below:
<MessageResponse>
<Status>SUCCESS</Status>
<Messages>
<a:Message>
<a:message>msg1</a:message>
<a:MsgID>1</a:MsgID>
</a:Message>
</Messages>
</MessageResponse>
My Service Contract is Like below: and one more important thing is:
MessageDco class is a copy of Message Class
[ServiceContract(Namespace="")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class UserLoginProfileService
{
[WebGet(UriTemplate = "/GetMessages?MsgID={MsgID}")]
public MessageResponse GetMessage(Guid MsgID)
{
MessageResponse objMessageResponse = new MessageResponse();
try
{
Message[] objMessage = new MessageFacade().GetMessage(MsgIDs);
if (objUserLoginProfile != null)
{
MessageResponse.Status = Status.Success;
MessageResponse.Messages =Mapper.ToDataContractObjects(objMessage);
}
else
{
objMessageResponse.Status = Status.Success;
}
}
catch (Exception ex)
{
objMessageResponse.Status = Status.Failure;
}
return objMessageResponse;
}
}
public class MessageResponse
{
[XmlElement(ElementName = "Messages")]
[DataMember(Name = "Messages")]
public MessageDco[] Messages { get; set; }
[XmlElement(ElementName = "Status")]
[DataMember(Name = "Status")]
public string Status { get; set; }
}
My mapper class is like below:
public sealed class Mapper
{
public static MessageDco[] ToDataContractObjects(IEnumerable<MessageDco> objMessageDco)
{
if (objMessageDco != null)
{
return objMessageDco.Select(a => ToDataContractObjects(a)).ToArray();
}
else
{
return null;
}
}
}
if we see "a:" is added to the tags. why it is added? i am unable remove that "a:" from the tags. Please help me how to remove "a:" from Tags. And also if you see MessageResponse class (Status tag) is coming correct But array list(i.e: Message[]) in MessageResponse is Coming wrong.
Thanks in advance.
The a is a generated namespace for your elements.
For the DataContractSerializer you can remove the namespace for your types in your service definition using the ServiceContractAttribute like so:
[ServiceContract(Namespace="")]

Categories

Resources