Edit
I have a problem at deserialising an XML file, containing inner classes (or nested classes).
I have following class diagram:
[XmlRoot(ElementName = "HM")]
public class OwnClass : BaseClass{
...
public OwnClass(){} // default constructor
...
}
I have the following _xmlMessageFormatter declaration (based on System.Messaging):
this._xmlMessageFormatter = new System.Messaging.XmlMessageFormatter();
System.Type[] OwnTypes = new System.Type[30];
OwnTypes[0] = typeof(Baseclasses.OwnClass); /* TR */
...
this._xmlMessageFormatter.TargetTypes = OwnTypes;
Edit: this is what the XML looks like:
<?xml version="1.0"?>
<HM xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>124</ID>
<TC>TR</TC>
</HM>
All of this is working fine.
Now I add a new class inside the definition of OwnClass:
[XmlRoot(ElementName = "HM")]
public class OwnClass : BaseClass {
[XmlElement(ElementName = "INS")]
public ClassInside f_Inside;
...
public OwnClass(){} // default constructor
...
public class ClassInside{
...
public class ClassInside(){}
...} // end of ClassInside
} // end of OwnClass
I've also added the corresponding targettype:
OwnTypes [27] = typeof(BaseClasses.OwnClass.ClassInside); // the number of the array is correct.
Edit: the XML file looks now as follows:
<?xml version="1.0"?>
<HM xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>125</ID>
<TC>TR</TC>
<TR>
<ID>1</ID>
<CD>MOVE</CD>
</TR>
</HM>
The _xmlMessageFormatter cannot handle the deserialisation, as you can see here:
Source code:
Object temp;
temp = this._xmlMessageFormatter.Read(message);
For getting more information, I've typed ? temp = this._xmlMessageFormatter.Read(message); in the immediate window (I'm working with Visual Studio), this is what I get:
'temp = this._xmlMessageFormatter.Read(message)' threw an exception of type 'System.InvalidOperationException'
Data: {System.Collections.ListDictionaryInternal}
HResult: -2146233079
HelpLink: null
InnerException: {"There was an error reflecting field 'f_Inside'."}
Message: "There was an error reflecting type 'BaseClasses.AnotherClass'."
Source: "System.Xml"
StackTrace: " at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter)\r\n at
System.Xml.Serialization.XmlReflectionImporter.ImportElement(TypeModel model, XmlRootAttribute root, String defaultNamespace, RecursionLimiter limiter)\r\n at
System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type, XmlRootAttribute root, String defaultNamespace)\r\n at
System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace)\r\n at
System.Messaging.XmlMessageFormatter.CreateTargetSerializerTable()\r\n at
System.Messaging.XmlMessageFormatter.Read(Message message)"
TargetSite: {System.Xml.Serialization.TypeMapping ImportTypeMapping(System.Xml.Serialization.TypeModel,
System.String,
ImportContext,
System.String,
System.Xml.Serialization.XmlAttributes,
Boolean,
Boolean,
System.Xml.Serialization.RecursionLimiter)}
I have two issues with the error message:
It mentions f_Inside. This looks correct, but I have used f_Inside as a general fieldname for all my classes, and the reason I mention this:
It mentions AnotherClass while I have send a message of the form OwnClass.
=> I'm having serious doubts about the correctness of the error message. Is there anybody who knows what I can do now (or how the _xmlFormatter works?)
Edit: added background
All of this is part of a messaging service: one application is sending a message, the other one is receiving it (using the System.Messaging.MessageQueue objects). The serialisation/deserialisation is just a part of it.
Thanks in advance
The problem is solved and it had nothing to do with deserialisation of nested classes:
In one of my classes (AgainAnotherClass), I had following source code:
[XmlElement(ElementName = "SA")]
[XmlElement(ElementName = "SA")]
public string SomeAttribute { get; set; }
(a typical case of Copy/Paste)
The fact that I had two lines with XmlElement caused the problem.
The exception looked as follows:
InnerException: {"There was an error reflecting field 'f_Inside'."}
Message: "There was an error reflecting type '<NameSpace>.AgainOtherClass'."
The InnerException made me believe that there was a problem with the nested class, while the Message spoke about a completely other class. I decided to follow the InnerException.
That was wrong! So, in case of C# exceptions where InnerException and Message contradict each other, first check the Message, then (maybe) the InnerException.
Related
I have an XML file which starts something like this:-
<?xml version="1.0" encoding="UTF-8"?>
<Deal xmlns="http://schemas.datacontract.org/2004/07/DealioCapLinkLib.Dealio.Models" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<AccountingDate>2019-09-30</AccountingDate>
When I try to convert this object to XML like below, I get an error:-
private static void Prod_Error_Test()
{
string prodRequestXml = File.ReadAllText("ProdXml.xml");
var serializer = new XmlSerializer(typeof(Service.Deal));
Service.Deal request ;
var reader = new StringReader(prodRequestXml);
request = (Service.Deal)serializer.Deserialize(reader);
}
The Error Message is "There is an error in XML document (2, 2).". The Inner Exception Message is "<Deal xmlns='http://schemas.datacontract.org/2004/07/DealioCapLinkLib.Dealio.Models'> was not expected."
Service.Deal is a WCF Proxy.So I may not be able to add any attributes. Can anyone suggest what to do here ?
Being a WCF proxy doesn't preclude adding attributes; in particular, it is usually a partial class, which means you can have your own separate file, with:
namespace Service
{
[XmlRoot("Deal", Namespace = "http://schemas.datacontract.org/2004/07/DealioCapLinkLib.Dealio.Models")]
partial class Deal {}
}
But ultimately: if the type doesn't conveniently fit the XML: stop fighting it - create a new separate type that fits the XML and works well with XmlSerializer, and then map between the two types in your own code.
I added a second method to my WCF service. It essentially does the same job as the other one except that it receives an xml document, deserialise it, and call the other method. It works perfectly locally, my XML is deserialized and the call succeeds. However, now that I have deployed it on my on-premise server, the call returns a 500 error because the deserialization failed.
My XML document has namespaces associated with every node, the root, and sub-root element have the "ns1" et all the others "ns2" prefix. To do the deserialization I (for now at least) hardcoded the namespaces for each node.
The troncated xml document:
<ns1:ValiderEtEnrichirGlobalEchangePartage
xmlns:ns1="API:WebApi"
xmlns:ns0="http://www.ra.fr/API/Transport/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns1:messageGlobal>
<ns0:AuteurEchange>...</ns0:AuteurEchange>
<ns0:Documents>
<ns0:DocumentEchangePartage>...</ns0:DocumentEchangePartage>
</ns0:Documents>
<ns0:ExpediteurEchange>...</ns0:ExpediteurEchange>
</ns1:messageGlobal>
</ns1:ValiderEtEnrichirGlobalEchangePartage>
The deserialising process in the service's method:
public GlobalEchangePartageValide ValiderEtEnrichirGlobalEchangePartageXML(string xmlMessageGlobal)
{
XmlRootAttribute xroot = new XmlRootAttribute();
xroot.ElementName="ValiderEtEnrichirGlobalEchangePartage";
xroot.Namespace="API:WebApi";
XmlSerializer serializer = new XmlSerializer(typeof(ValiderEtEnrichirGlobalEchangePartage),xroot );
StringReader stringReader = new StringReader(xmlMessageGlobal);
ValiderEtEnrichirGlobalEchangePartage messageGlobal = (ValiderEtEnrichirGlobalEchangePartage)serializer.Deserialize(stringReader);
return ValiderEtEnrichirGlobalEchangePartage(messageGlobal.GlobalEchangePartage);
}
The class coresponding to the xml root element:
[XmlRootAttribute("ValiderEtEnrichirGlobalEchangePartage")]
public class ValiderEtEnrichirGlobalEchangePartage
{
[XmlElement(ElementName=("messageGlobal"))]
public GlobalEchangePartage GlobalEchangePartage { get; set; }
}
The class of xml sub root element:
[DataContract(Namespace = NamespacesConstantes.NAMESPACE_TRANSPORT)]
public class GlobalEchangePartage
{
[DataMember]
[XmlElement(ElementName = ("AuteurEchange"), Namespace = "http://www.ra.fr/API/Transport/")]
public Auteur AuteurEchange { get; set; }
[DataMember]
[XmlElement(ElementName = ("ExpediteurEchange"), Namespace = "http://www.ra.fr/API/Transport/")]
public Auteur ExpediteurEchange { get; set; }
[DataMember]
[XmlArray(ElementName="Documents", Namespace = "http://www.ra.fr/API/Transport/")]
[XmlArrayItem("DocumentEchangePartage")]
public List<DocumentEchangePartage> Documents { get; set; }
}
The error I get is in French very ambiguous but can be approximatively translated by :
WCF error : System.ServiceModel.Dispatcher.NetDispatcherFaultException: The formatting module generated an exception when trying to deserialize the
message: an error occurred when trying to deserialize the
API:WebApi:xmlMessageGlobal parameter. The InnerException message was 'An
error occurred when deserializing the System.String object. Ending (TN :
last /final ) element 'xmlMessageGlobal' from the namespace 'API:WebApi'
expected. Found element 'ns1:ValiderEtEnrichirGlobalEchangePartage' from the
namespace "API:WebApi'.
Original :
WCF error : System.ServiceModel.Dispatcher.NetDispatcherFaultException: Le
module de formatage a généré une exception en tentant de désérialiser le
message : Une erreur s'est produite en tentant de désérialiser le paramètre
API:WebApi:xmlMessageGlobal. Le message InnerException était 'Une
erreur s'est produite lors de la désérialisation de l'objet de type
System.String. Élément de fin 'xmlMessageGlobal' provenant de l'espace de
noms 'API:WebApi' attendu. Trouvé élément
'ns1:ValiderEtEnrichirGlobalEchangePartage' provenant de l'espace de noms
'API:WebApi'.'.
Notice how it says that 'xmlMessageGlobal' is expected in the XML document while it is the variable's name ...
Thanks (for reading) a lot !
PS : If the French error message could be put in something that collapses it, I would appreciate, i didn't find a way to do it.
The problem encountered was indeed a deserialization problem. However, it wasn't about the piece of code I had written, it was when the string parameter was beeing received. Because my xml doc was an wrapped in another xml document (the request) there was issues in the processing. By base64 encoding my XML in my Logic App, and decoding it in the service i was able to fix the deserialization issue.
TLDR : be careful when you send xml through a String parameter.
I have automatically generated a C# class using the visual studio wsdl.exe tool starting from supplied wsdl and xsd files.
I want to use this client to call a java SOAP service in Italy created by SII (Servizio Informativo integrato).
I am getting this error.
System.Web.Services.Protocols.SoapHeaderException:
'InRequestHandler[pdcInterfacciaServiziSII]:
Il MessaggioSII fornito all'interfaccia Servizi SII non risulta conforme alla struttura standard:
Eccezione SII_EX_ERROR con codice [SII_AU_004] - EccezioneValidazioneProtocollo,
descrizione errore: MessaggioSII ; MessaggioSII without attribute namespace declaration
http://www.acquirenteunico.it/schemas/2010/SII_AU/MessaggioSII'
Translation:
The MessaggioSII supplied to the SII Services Interface does not match with the standard structure:
Exception SII_EX_ERROR with code [SII_AU_004] -
ProtocolValidationException, Error description: MessaggioSII ;
MessaggioSII without attribute namespace declaration
http://www.acquirenteunico.it/schemas/2010/SII_AU/MessaggioSII'
I have also the Java code that calls the same SOAP service and it works perfectly.
This is the relevant part of the C# code automatically generated by the wsdl.exe tool. This code fails with the error written above.
public VerificaInteroperabilitaPdCService() {
this.Url = "http://127.0.0.1:8080/openspcoop2/pdc/PD";
}
public event startCompletedEventHandler startCompleted;
[System.Web.Services.Protocols.SoapRpcMethodAttribute("start", RequestNamespace="http://www.acquirenteunico.it/schemas/2010/SII_AU/VerificaInteroperabilitaPdC", ResponseNamespace="http://www.acquirenteunico.it/schemas/2010/SII_AU/VerificaInteroperabilitaPdC", Use=System.Web.Services.Description.SoapBindingUse.Literal)]
[return: System.Xml.Serialization.XmlElementAttribute("MessaggioSII")]
public StartResponseMessaggioSIIType start(StartMessaggioSIIType MessaggioSII) {
object[] results = this.Invoke("start", new object[] {
MessaggioSII});
return ((StartResponseMessaggioSIIType)(results[0]));
}
public System.IAsyncResult Beginstart(StartMessaggioSIIType MessaggioSII, System.AsyncCallback callback, object asyncState) {
return this.BeginInvoke("start", new object[] {
MessaggioSII}, callback, asyncState);
}
public StartResponseMessaggioSIIType Endstart(System.IAsyncResult asyncResult) {
object[] results = this.EndInvoke(asyncResult);
return ((StartResponseMessaggioSIIType)(results[0]));
}
The SOAP client should be able to connect to the SOAP service, send a question and get an answer
I've been using xsd2code v3.4.
So far I'm pretty close to getting it to work, however I'm facing one glaring issue and I can't seem to find any solutions. When my XML gets generated after I serialize my object, it's adding an additional complex type that is named exactly like the class. This is what I currently get. Notice how it's adding an unnecessary collection right after an order line:
<?xml version="1.0" encoding="utf-8"?>
<CORE_PO_INBOUND_V2 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<INTEGRATION_MESSAGE_CONTROL>
<ACTION>FULL_UPDATE</ACTION>
<COMPANY_CODE>COMPANY</COMPANY_CODE>
<ORG_CODE>COMPANY</ORG_CODE>
<MESSAGE_TYPE>INBOUND_ENTITY_INTEGRATION</MESSAGE_TYPE>
<USERID>COMPANY</USERID>
<RECEIVER>TA15</RECEIVER>
<SENDER>COMPANY</SENDER>
<BATCH_ID>1234</BATCH_ID>
<BUS_KEY>
<ORG_CODE>COMPANY</ORG_CODE>
<PO_NUMBER>1234</PO_NUMBER>
</BUS_KEY>
</INTEGRATION_MESSAGE_CONTROL>
<PURCHASE_ORDER_HEADER>
<CTRY_OF_EXPORT>TR</CTRY_OF_EXPORT>
<CTRY_OF_IMPORT>US</CTRY_OF_IMPORT>
<CURRENCY_CODE>USD</CURRENCY_CODE>
<INCOTERM_CODE>011</INCOTERM_CODE>
<ORG_CODE>COMPANY</ORG_CODE>
<SOURCE_TX_ID>THING</SOURCE_TX_ID>
<PO_NUMBER>1234</PO_NUMBER>
<PURCHASE_ORDER_LINE>
<CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_LINE>
<BUSINESS_UNIT>BCA</BUSINESS_UNIT>
<COMMERCIAL_UOM>EA</COMMERCIAL_UOM>
<CTRY_OF_IMPORT>US</CTRY_OF_IMPORT>
<CURRENCY_CODE>USD</CURRENCY_CODE>
<DEPARTMENT>602</DEPARTMENT>
<LINE_ID>1</LINE_ID>
</CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_LINE>
</PURCHASE_ORDER_LINE>
<PURCHASE_ORDER_HEADER_PARTNER>
<CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_HEADER_PARTNER>
<REF_RESOLUTION_PARTNER>Stuff</REF_RESOLUTION_PARTNER>
</CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_HEADER_PARTNER>
</PURCHASE_ORDER_HEADER_PARTNER>
</PURCHASE_ORDER_HEADER>
</CORE_PO_INBOUND_V2>
This is what I actually want:
<?xml version="1.0" encoding="utf-8"?>
<CORE_PO_INBOUND_V2 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<INTEGRATION_MESSAGE_CONTROL>
<ACTION>FULL_UPDATE</ACTION>
<COMPANY_CODE>COMPANY</COMPANY_CODE>
<ORG_CODE>COMPANY</ORG_CODE>
<MESSAGE_TYPE>INBOUND_ENTITY_INTEGRATION</MESSAGE_TYPE>
<USERID>COMPANY</USERID>
<RECEIVER>TA15</RECEIVER>
<SENDER>COMPANY</SENDER>
<BATCH_ID>1234</BATCH_ID>
<BUS_KEY>
<ORG_CODE>COMPANY</ORG_CODE>
<PO_NUMBER>1234</PO_NUMBER>
</BUS_KEY>
</INTEGRATION_MESSAGE_CONTROL>
<PURCHASE_ORDER_HEADER>
<CTRY_OF_EXPORT>TR</CTRY_OF_EXPORT>
<CTRY_OF_IMPORT>US</CTRY_OF_IMPORT>
<CURRENCY_CODE>USD</CURRENCY_CODE>
<INCOTERM_CODE>011</INCOTERM_CODE>
<ORG_CODE>COMPANY</ORG_CODE>
<SOURCE_TX_ID>THING</SOURCE_TX_ID>
<PO_NUMBER>1234</PO_NUMBER>
<PURCHASE_ORDER_LINE>
<BUSINESS_UNIT>BCA</BUSINESS_UNIT>
<COMMERCIAL_UOM>EA</COMMERCIAL_UOM>
<CTRY_OF_IMPORT>US</CTRY_OF_IMPORT>
<CURRENCY_CODE>USD</CURRENCY_CODE>
<DEPARTMENT>602</DEPARTMENT>
<LINE_ID>1</LINE_ID>
</PURCHASE_ORDER_LINE>
<PURCHASE_ORDER_HEADER_PARTNER>
<REF_RESOLUTION_PARTNER>Stuff</REF_RESOLUTION_PARTNER>
</PURCHASE_ORDER_HEADER_PARTNER>
</PURCHASE_ORDER_HEADER>
</CORE_PO_INBOUND_V2>
Is there some setting I'm using incorrectly? I have it set to work with List collections. Seems as if this problem only exists for collections of a class that's generated from this tool.
Edit: Adding some snippets of the designer class that gets generated by xsd2code. Note that this file is pretty large (almost 10k lines...), so I'm not going to post the whole thing here, but rather the chunks that pertain to the purchase order line collections of elements:
public partial class CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADER : EntityBase<CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADER>
{
private List<CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_LINE> pURCHASE_ORDER_LINEField;
public List<CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_LINE> PURCHASE_ORDER_LINE
{
get
{
if ((this.pURCHASE_ORDER_LINEField == null))
{
this.pURCHASE_ORDER_LINEField = new List<CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_LINE>();
}
return this.pURCHASE_ORDER_LINEField;
}
set
{
this.pURCHASE_ORDER_LINEField = value;
}
}
}
public partial class CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_LINE : EntityBase<CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_LINE>
{
private System.Nullable<decimal> aREAField;
private bool aREAFieldSpecified;
private string aREA_UOMField;
...
}
I think I found a solution. I spoke with a colleague whom had done something similar. He said he used the native "xsd" and not "xsd2code". We did a compare on what got generated and noticed that on the arrays (in my case, I use lists...), he had the following annotation:
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)]
Is there a way to trigger this same annotation via xsd2code? Looks like without it extra elements get generated upon executing the serializer.
I have following classes:
[Serializable]
public class TradeBotSettings
{
public ExchangePlatform Exchange
{
get;
set;
}
}
[Serializable]
public enum ExchangePlatform
{
[XmlEnum("BTC_E")]
BTC_E,
[XmlEnum("BitStamp")]
BitStamp,
[XmlEnum("CampBX")]
CampBX,
[XmlEnum("Cryptsy")]
Cryptsy,
[XmlEnum("BTCChina")]
BTCChina,
}
When i try to serialize gives error
private void Button2_Click(object sender, EventArgs e)
{
TradeBotSettings tbSettings = new TradeBotSettings();
tbSettings.Exchange = ExchangePlatform.BTC_E;
StreamWriter sw = new StreamWriter(#"D:\Temp\Trader\Trader\Trader\bin\x86\Debug\configs\bots.xml", false);
xmlSerializerTradebot = new XmlSerializer(tbSettings.GetType());
xmlSerializerSettings.Serialize(sw, tbSettings);
sw.Close();
}
Error is : An unhandled exception of type 'System.InvalidOperationException' occurred in System.Xml.dll
Additional information: There was an error generating the XML document.
This looks like a generic error anyone have a clue about this
Thanks in advance
The code you presented wont compile, xmlSerializerSettings is unknown. This probably should have been "xmlSerializerTradebot.Serialize(..." instead, and this works fine. Maybe that´s your problem?
BTW: You should use the "using" clause when creating StreamWriter instances to prevent having the file not immediately closed in case of serialization exceptions. You also dont need to use the XMLEnum attribute unless you want to have the default serialization behaviour for enums changed...
BTW2: Yeah, i know, this is not a clear answer to the problem and i should rather comment. I would if i already could... ;-)