I have an XML that I want to deserialize according to my own classes. It deserializes properly, but some of the values become null. It doesn't give an errors, and I'm not sure where the error lies.
I've tried changing the classes, serializing a memory model and then checking the output, but none of it worked to my liking. It needs to follow the XML that is provided.
My model:
[XmlRoot(ElementName = "model", Namespace = "http://www.archimatetool.com/archimate")]
public class Model
{
[XmlElement(ElementName = "folder")]
public List<Folder> Folders { get; set; }
[XmlElement(ElementName = "purpose")]
public string Purpose { get; set; }
[XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Xsi { get; set; }
[XmlAttribute(AttributeName = "archimate", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Archimate { get; set; }
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "version")]
public string Version { get; set; }
}
My XML
<?xml version="1.0" encoding="UTF-8"?>
<archimate:model xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:archimate="http://www.archimatetool.com/archimate" name="Archisurance" id="11f5304f" version="3.1.1">
<folder name="Business" id="8c90fdfa" type="business">
<folder name="Actors" id="fa63373b">
<element xsi:type="archimate:BusinessInterface" id="1544" name="mail"/>
</folder>
</folder>
<purpose>An example of a fictional Insurance company.</purpose>
</archimate:model>
This the result im getting after deserializing.
I cant post pictures (due to reputation) so i am just posting a link.
result
I would expect the purpose field to say "An example of a fictional Insurance company", but it is null.
You can deserialize your XML with the following data model:
[XmlRoot(ElementName = "model", Namespace = "http://www.archimatetool.com/archimate")]
[XmlType(Namespace = "http://www.archimatetool.com/archimate")]
public class Model
{
[XmlElement(ElementName = "folder", Form = XmlSchemaForm.Unqualified)]
public List<Folder> Folders { get; set; }
[XmlElement(ElementName = "purpose", Form = XmlSchemaForm.Unqualified)]
public string Purpose { get; set; }
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "version")]
public string Version { get; set; }
}
[XmlType(Namespace = "http://www.archimatetool.com/archimate")]
public class Folder
{
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "type")]
public string Type { get; set; }
[XmlElement(ElementName = "folder", Form = XmlSchemaForm.Unqualified)]
public List<Folder> Folders { get; set; }
[XmlElement(ElementName = "element", Form = XmlSchemaForm.Unqualified)]
public List<Element> Element { get; set; }
}
[XmlType(Namespace = "http://www.archimatetool.com/archimate")]
[XmlInclude(typeof(BusinessInterface))]
public abstract class Element
{
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
}
[XmlType(TypeName = "BusinessInterface", Namespace = "http://www.archimatetool.com/archimate")]
public class BusinessInterface : Element
{
}
Notes:
The root element <archimate:model> is in the archimate: namespace, but its child elements are not in any namespace, since the archimate: namespace is not the default namespace. Thus it's necessary to indicate to XmlSerializer that these child elements are in a different namespace from their parent. Setting XmlElementAttribute.Form = XmlSchemaForm.Unqualified accomplishes this.
(It is not necessary to specify that attributes are in the default namespace, since all XML attributes are assumed to be unqualified unless otherwise specified.)
The presence of the xsi:type="archimate:BusinessInterface" attribute indicates the <element> attribute is part of a polymorphic type hierarchy. The xsi:type attribute is a standard w3c attribute that allows an element to explicitly assert its type. XmlSerializer supports this attribute and in fact requires the presence of a subtype corresponding to the xsi:type and declared through the [XmlInclude] attribute.
For details see How to: Control Serialization of Derived Classes.
Here I made an arbitrary choice of properties to include in the base class Element and the derived class BusinessInterface. You may need to adjust this choice given a more complete XML sample.
Sample fiddle here.
Related
I have the following XML class structure:
[XmlRoot(ElementName = "Envelope", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class Envelope
{
[XmlElement(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public Body Body { get; set; }
}
[XmlRoot(ElementName = "Body")]
public class Body
{
[XmlElement(ElementName = "CustomerBundleMaintainConfirmation_sync_V1", Namespace = "http://sap.com/xi/SAPGlobal20/Global")]
public CustomerMaintainConfirmation CustomerMaintainConfirmation { get; set; }
}
[XmlRoot(ElementName = "CustomerBundleMaintainConfirmation_sync_V1")]
public class CustomerMaintainConfirmation
{
[XmlElement(ElementName = "Log")]
public Log Log { get; set; }
}
[XmlRoot(ElementName = "Log")]
public class Log
{
[XmlElement(ElementName = "MaximumLogItemSeverityCode")]
public string MaximumLogItemSeverityCode { get; set; }
[XmlElement(ElementName = "Item")]
public Item Item { get; set; }
}
[XmlRoot(ElementName = "Item")]
public class Item
{
[XmlElement(ElementName = "TypeID")]
public string TypeID { get; set; }
[XmlElement(ElementName = "CategoryCode")]
public string CategoryCode { get; set; }
[XmlElement(ElementName = "SeverityCode")]
public string SeverityCode { get; set; }
[XmlElement(ElementName = "Note")]
public string Note { get; set; }
}
And this is the XML I am working with:
<?xml version="1.0" encoding="UTF-8"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Header />
<soap-env:Body>
<n0:CustomerBundleMaintainConfirmation_sync_V1 xmlns:n0="http://sap.com/xi/SAPGlobal20/Global" xmlns:prx="urn:sap.com:proxy:LBK:/1SAI/TAE6F3228CC6D723FF1823E:804">
<Log>
<MaximumLogItemSeverityCode>3</MaximumLogItemSeverityCode>
<Item>
<TypeID>018</TypeID>
<CategoryCode>YES</CategoryCode>
<SeverityCode>0</SeverityCode>
<Note>TestNotes</Note>
</Item>
</Log>
</n0:CustomerBundleMaintainConfirmation_sync_V1>
</soap-env:Body>
</soap-env:Envelope>
When I attempt to deserialize this data into my classes, for whatever reason the "Log" class is null. Envelope, Body and CustomerMaintainConfirmation are all populated correctly. I see no reason why this is the case, but I've been staring at this for so long as this point I'm absolutely sure I am missing a mistake in and amongst my code somewhere.
This last piece of code is inside of the method that does the actual deserializing:
XmlSerializer serializer = new XmlSerializer(typeof(Envelope));
using (StreamReader responseReader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{
return (Envelope)serializer.Deserialize(responseReader);
}
I'm still working through it, but if anyone could point out any issues they might see with what I've provided, please let me know.
It's a namespace inconsistency. It deserializes with this change:
[XmlRoot(ElementName = "CustomerBundleMaintainConfirmation_sync_V1")]
public class CustomerMaintainConfirmation
{
[XmlElement(ElementName = "Log", Namespace = "")]
public Log Log { get; set; }
}
You just miss the empty namespace indication on your element:
[XmlRoot(ElementName = "CustomerBundleMaintainConfirmation_sync_V1")]
public class CustomerMaintainConfirmation
{
[XmlElement(ElementName = "Log", Namespace = "")]
public Log Log { get; set; }
}
This is required, as apparently, your Log element has the empty namespace even though the CustomerBundleMaintainConfirmation_sync_V1 above has a nonempty one.
That's the only change in your code to make it work as shown in a fiddle.
We have a WCF service with defined class (class was autogenerated from XML using http://xmltocsharp.azurewebsites.net/ ):
namespace CRMtoQLM.DAL
{
[Serializable]
[DataContract(Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class Envelope
{
[XmlElement(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
[DataMember]
public Body Body { get; set; }
[XmlAttribute(AttributeName = "soapenv", Namespace = "http://www.w3.org/2000/xmlns/")]
[DataMember]
public string Soapenv { get; set; }
[XmlAttribute(AttributeName = "xsd", Namespace = "http://www.w3.org/2000/xmlns/")]
[DataMember]
public string Xsd { get; set; }
[XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
[DataMember]
public string Xsi { get; set; }
}
//[Serializable]
[XmlRoot(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
[DataContract(Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class Body
{
[XmlElement(ElementName = "notifications", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataMember]
public Notifications Notifications { get; set; }
}
//[Serializable]
[XmlRoot(ElementName = "notifications", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataContract(Namespace = "http://soap.sforce.com/2005/09/outbound")]
public class Notifications
{
[XmlElement(ElementName = "OrganizationId", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataMember]
public string OrganizationId { get; set; }
[XmlElement(ElementName = "ActionId", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataMember]
public string ActionId { get; set; }
[XmlElement(ElementName = "SessionId", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataMember]
public string SessionId { get; set; }
[XmlElement(ElementName = "EnterpriseUrl", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataMember]
public string EnterpriseUrl { get; set; }
[XmlElement(ElementName = "PartnerUrl", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataMember]
public string PartnerUrl { get; set; }
[XmlElement(ElementName = "Notification", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataMember]
public Notification Notification { get; set; }
[XmlAttribute(AttributeName = "xmlns")]
[DataMember]
public string Xmlns { get; set; }
}
//[Serializable]
[XmlRoot(ElementName = "Notification", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataContract(Namespace = "http://soap.sforce.com/2005/09/outbound")]
public class Notification
{
[XmlElement(ElementName = "Id", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataMember]
public string Id { get; set; }
[XmlElement(ElementName = "sObject", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataMember]
public SObject SObject { get; set; }
}
//[Serializable]
[XmlRoot(ElementName = "sObject", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataContract(Namespace = "http://soap.sforce.com/2005/09/outbound")]
public class SObject
{
[XmlElement(ElementName = "Id", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
[DataMember]
public string Id { get; set; }
[XmlElement(ElementName = "Asset_Account_City__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
[DataMember]
public string Asset_Account_City__c { get; set; }
[XmlElement(ElementName = "Asset_Account_Country__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
[DataMember]
public string Asset_Account_Country__c { get; set; }
[XmlElement(ElementName = "Asset_Account_Name__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
[DataMember]
public string Asset_Account_Name__c { get; set; }
[XmlElement(ElementName = "Asset_Customer_ID__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
[DataMember]
public string Asset_Customer_ID__c { get; set; }
[XmlElement(ElementName = "Expiration_Date__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
[DataMember]
public string Expiration_Date__c { get; set; }
[XmlElement(ElementName = "License__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
[DataMember]
public string License__c { get; set; }
[XmlElement(ElementName = "Reader_Code__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
[DataMember]
public string Reader_Code__c { get; set; }
[XmlElement(ElementName = "Reader_Quantity__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
[DataMember]
public string Reader_Quantity__c { get; set; }
[XmlAttribute(AttributeName = "type", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
[DataMember]
public string Type { get; set; }
[XmlAttribute(AttributeName = "sf", Namespace = "http://www.w3.org/2000/xmlns/")]
[DataMember]
public string Sf { get; set; }
}
}
IService.cs defined like this:
[OperationContract]
[WebInvoke(Method = "POST",
UriTemplate = "Test",
RequestFormat = WebMessageFormat.Xml)]
//[XmlSerializerFormat]
string Test(Envelope parameter);
and implementation:
public string Test(Envelope parameter)
{
return "";
}
but "parameter" in Test function contains NULL objects. I know there is a problem with XML deserialization but can't figure out where exactly.
EDIT: this is the request that I'm sending via Postman
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<notifications xmlns="http://soap.sforce.com/2005/09/outbound">
<OrganizationId>123456</OrganizationId>
<ActionId>123456</ActionId>
<SessionId>123456</SessionId>
<EnterpriseUrl>https://eu8.salesforce.com/</EnterpriseUrl>
<PartnerUrl>https://eu8.salesforce.com/</PartnerUrl>
<Notification>
<Id>123456</Id>
<sObject xsi:type="sf:Asset" xmlns:sf="urn:sobject.enterprise.soap.sforce.com">
<sf:Id>123456</sf:Id>
<sf:Asset_Account_City__c>123456</sf:Asset_Account_City__c>
<sf:Asset_Account_Country__c>123456</sf:Asset_Account_Country__c>
<sf:Asset_Account_Name__c>123456</sf:Asset_Account_Name__c>
<sf:Asset_Customer_ID__c>123456</sf:Asset_Customer_ID__c>
<sf:Expiration_Date__c>123456</sf:Expiration_Date__c>
<sf:License__c>123456</sf:License__c>
<sf:Reader_Code__c>123456</sf:Reader_Code__c>
<sf:Reader_Quantity__c>123456</sf:Reader_Quantity__c>
</sObject>
</Notification>
</notifications>
</soapenv:Body>
</soapenv:Envelope>
SOAP-based bindings unpack the SOAP envelope for you, so you shouldn't need to define the SOAP envelope types. The WebHttpBinding which you say you're using, which doesn't know anything about SOAP, would expect that the contract type (the argument of your service method IService.Test) would match the entire HTTP body, so I see the logic of what you're doing, however I don't think it's necessary to work against the framework like this.
To fix this scenario do a couple of things:
amend your implementation of IService.Test to take an argument of type Notifications
string Test(Notifications notifications)
amend the service config to specify an HTTP-based SOAP binding, probably BasicHttpBinding
<endpoint address="/relativeaddress/" binding="basicHttpBinding" ... />
I think a SOAP binding will probably ignore WebInvokeAttribute, but to be on the safe side, remove it. It's superfluous for a SOAP binding because SOAP is always POST.
I don't think you need to delete the Envelope and Body types but they won't do anything if they're not referenced, which is what you want. WCF bindings understand the SOAP envelope natively and don't need it to be specified, it's the content of the SOAP Body element that types need to be provided for.
Your XML sample contains a single instance of Notification under Notifications but the name implies that this child element could be repeating - if it is, I don't think the generated classes will work for you because the Notifications class has a singular Notification property - the generator you linked to has no way to know this so I don't fault it in any way. I added a copy of the Notification element as a sibling and ran it through again - this time it generated a list member:
[XmlElement(ElementName="Notification", Namespace="http://soap.sforce.com/2005/09/outbound")]
public List<Notification> Notification { get; set; }
You can use the xsd.exe tool supplied with Visual Studio (I think) to generate classes - see the documentation. I wouldn't be surprised if that web tool is driving this behind the scenes but I'd trust xsd.exe to generate classes that have the best possible compliance with what WCF expects of them.
I'm trying to deserialize an XML file with XmlSerializer. However i am getting this exception: There is an error in XML document (1, 41).InnerException Message "ReplicationStatus xmlns='DistributionServices' was not expected."
The XML file looks like this:
<?xml version="1.0" encoding="UTF-8" ?>
<ts:Status xmlns:ts="DistributionServices">
<Server>DUMMY</Server>
<Object>DUMMY</Object>
<Port>123</Port>
<Code>DUMMY</Code>
<Key>b0ed5e56</Key>
</ts:Status>
The code that I have used is as follows:
MessageData data = new MessageData();
XmlSerializer xmlSerializer = new XmlSerializer(data.GetType());
data = (MessageData)xmlSerializer.Deserialize(new StringReader(msgData));
Here, msgData is the string containing the xml shown above.
MessageData class looks like this:
[Serializable,XmlType("Status")]
public class MessageData
{
[XmlElement("Server")]
public string Server { get; set; }
[XmlElement("Object")]
public string Object { get; set; }
[XmlElement("Port")]
public string Port { get; set; }
[XmlElement("Code")]
public string Code { get; set; }
[XmlElement("Key")]
public string Key { get; set; }
}
Please let me know what I am doing wrong.
You have to declare the namespace in your class and set it to empty on your properties. Change your class model to this and it should work fine.
[Serializable, XmlRoot("Status", Namespace = "DistributionServices")]
public class MessageData
{
[XmlElement(Namespace = "")]
public string Server { get; set; }
[XmlElement(Namespace = "")]
public string Object { get; set; }
[XmlElement(Namespace = "")]
public string Port { get; set; }
[XmlElement(Namespace = "")]
public string Code { get; set; }
[XmlElement(Namespace = "")]
public string Key { get; set; }
}
BTW: you don't have to name XmlElement's explicit if they have the same name as the property.
I have a class object:
[XmlRoot(ElementName = "Tag")]
public class Tag
{
[XmlElement(ElementName = "TagId")]
public string TagId { get; set; }
[XmlElement(ElementName = "TagTitle")]
public string TagTitle { get; set; }
}
[XmlRoot(ElementName = "LocTags")]
public class LocTags
{
[XmlElement(ElementName = "Tag")]
public Tag[] Tag { get; set; }
}
[XmlRoot(ElementName = "test")]
public class test
{
[XmlElement(ElementName = "ID")]
public string ID { get; set; }
[XmlElement(ElementName = "Name")]
public string Name { get; set; }
[XmlElement(ElementName = "LocTags")]
public LocTags LocTags { get; set; }
}
And I have data already present like this:
test:
id=1
Name="abc"
locTags
tag
tagId=1
tagTitle="xyz"
id=2
name="qwe"
...
I would like to test=1 add new object to Tag, should get result:
test:
id=1
Name="abc"
locTags
tag
tagId=1
tagTitle="xyz"
tagId=2
tagTitle="pqr"
id=2
name="qwe"
...
How do I do that?
Edit
List<Tag> tagNew = test.locTags.Tag.ToList();
tagNew.Add(new Tag
{
TagTitle = "pqr",
TagId = "2"
});
test.locTags.Tag = tagNew;
but the last line gives me error:
Error 10 Cannot implicitly convert type 'System.Collections.Generic.List' to 'Tag[]'
Make Tag[] Tag as List<Tag> and then use test.LocTagXY.Tags.Add(newTag)
If you wish to stay with Arrays, use Pradeep Kumar's test.locTags.Tag = tagNew.ToArray()
Hi I have 3 classes like that:
public abstract class XmlNs
{
public const string XmlnsAttribute = "urn:ebay:apis:eBLBaseComponents";
}
[Serializable]
public class BulkDataExchangeRequests : XmlNs
{
[XmlAttribute("xmlns")]
public string XmlNs = XmlnsAttribute;
[XmlElement("Header")]
public Header Header { get; set; }
[XmlElement("AddFixedPriceItemRequest")]
public List<AddFixedPriceItemRequest> ListAddFixedPriceItemRequest { get; set; }
}
[Serializable]
public class AddFixedPriceItemRequest : XmlNs
{
[XmlElement("ErrorLanguage")]
public string ErrorLanguage { get; set; }
[XmlElement("WarningLevel")]
public string WarningLevel { get; set; }
[XmlElement("Version")]
public string Version { get; set; }
[XmlElement("Item")]
public ItemType Item { get; set; }
[XmlAttribute("xmlns")]
public string XmlNs = XmlnsAttribute;
}
The problem is that when I serialize the object I get a correct xml but with no xmlns attribute in the AddFixedPriceItemRequest item, while in the BulkDataExchangeRequests the xmlns is correctly written....
Any help will be very appreciated...
You are nesting elements, and nested elements are in the same namespace as their parent elements, if you don't specify anything else. So in fact your serializer is correct to not output the xmlns attribute again, as it is not needed.
See:
<root xmlns="my-namespace">
<element>this is also in the namespace "my-namespace" without further declaration</element>
<so><are><child><elements></elements></child></are></so>
</root>
EDIT:
Even though eBay is obviously not conforming to standards here, there is a solution! You can declare namespaces for the .NET xml serializer in a very convenient manner, and these declarations are kept, even if they are repeated:
[Serializable]
public class BulkDataExchangeRequests : XmlNs
{
[XmlElement("Header")]
public Header Header { get; set; }
[XmlElement("AddFixedPriceItemRequest")]
public List<AddFixedPriceItemRequest> ListAddFixedPriceItemRequest { get; set; }
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces(new System.Xml.XmlQualifiedName[] { new System.Xml.XmlQualifiedName("", XmlnsAttribute) });
}
[Serializable]
public class AddFixedPriceItemRequest : XmlNs
{
[XmlElement("ErrorLanguage")]
public string ErrorLanguage { get; set; }
[XmlElement("WarningLevel")]
public string WarningLevel { get; set; }
[XmlElement("Version")]
public string Version { get; set; }
[XmlElement("Item")]
public ItemType Item { get; set; }
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces(new System.Xml.XmlQualifiedName[] { new System.Xml.XmlQualifiedName("", XmlnsAttribute) });
}
The output is as expected:
<?xml version="1.0" encoding="utf-16"?>
<BulkDataExchangeRequests xmlns:xsi="http://www.w3.org/2001/XMLSchema-Instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:ebay:apis:eBLBaseComponents">
<AddFixedPriceItemRequest xmlns="urn:ebay:apis:eBLBaseComponents" />
<AddFixedPriceItemRequest xmlns="urn:ebay:apis:eBLBaseComponents" />
</BulkDataExchangeRequests>
Relevant documentation:
System.Xml.Serialization.XmlNamespaceDeclarations
System.Xml.Serialization.XmlSerializerNamespaces