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.
Related
please advice how do i map the response into class because from every example i came across body always don't have symbol like core:transactionResponse
my code:
string fileName = #"C:\Users\Lenovo\Downloads\GLResponseXml.xml";
XDocument xDoc = XDocument.Load(fileName);
var unwrappedResponse = xDoc.Descendants((XNamespace)"http://schemas.xmlsoap.org/soap/envelope/" + "Body")
.First()
.FirstNode;
XmlSerializer xmlSerializer = new XmlSerializer(typeof(TransactionResponse));
TransactionResponse response = (TransactionResponse)xmlSerializer.Deserialize(xDoc.Descendants((XNamespace)"http://schemas.xmlsoap.org/soap/envelope/" + "Body")
.First()
.FirstNode
.CreateReader()
);
for deserealizing this soap xml:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dpss0="bons">
<soapenv:Header/>
<soapenv:Body>
<core:transactionResponse xmlns:bo="http://service.example.co.id/core/bo" xmlns:core="http://service.example.co.id/core">
<response>
<header>
<coreJournal>149326</coreJournal>
</header>
<content xsi:type="bo:OKMessage">
<message/>
</content>
</response>
</core:transactionResponse>
</soapenv:Body>
</soapenv:Envelope>
but i got this error: InvalidOperationException: <transactionResponse xmlns='http://service.example.co.id/core'> was not expected.
i'm mapping the response into this class:
public class GLXmlResponse
{
[XmlRoot(ElementName = "response")]
public class Response
{
[XmlElement(ElementName = "header")]
public Header Header { get; set; }
[XmlElement(ElementName = "content")]
public Content Content { get; set; }
}
[XmlRoot(ElementName = "header")]
public class Header
{
[XmlElement(ElementName = "coreJournal")]
public string CoreJournal { get; set; }
}
[XmlRoot(ElementName = "content")]
public class Content
{
[XmlElement(ElementName = "message")]
public string Message { get; set; }
[XmlAttribute(AttributeName = "type", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string Type { get; set; }
}
[XmlRoot(ElementName = "transactionResponse", Namespace = "http://service.example.co.id/core")]
public class TransactionResponse
{
[XmlElement(ElementName = "response")]
public Response Response { get; set; }
[XmlAttribute(AttributeName = "bo", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Bo { get; set; }
[XmlAttribute(AttributeName = "core", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Core { get; set; }
}
[XmlRoot(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class Body
{
[XmlElement(ElementName = "transactionResponse", Namespace = "http://service.example.co.id/core")]
public TransactionResponse TransactionResponse { get; set; }
}
[XmlRoot(ElementName = "Envelope", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class Envelope
{
[XmlElement(ElementName = "Header", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public string Header { get; set; }
[XmlElement(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public Body Body { get; set; }
[XmlAttribute(AttributeName = "soapenv", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Soapenv { get; set; }
[XmlAttribute(AttributeName = "soapenc", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Soapenc { get; set; }
[XmlAttribute(AttributeName = "xsd", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Xsd { get; set; }
[XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Xsi { get; set; }
[XmlAttribute(AttributeName = "dpss0", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Dpss0 { get; set; }
}
}
need help on getting the class object right
I can't repro the exact error you're seeing, so you might want to check from a minimal repro; I'm using:
var env = (GLXmlResponse.Envelope)new XmlSerializer(typeof(GLXmlResponse.Envelope))
.Deserialize(new StringReader(xml));
System.Console.WriteLine(env.Body.TransactionResponse.Response.Header.CoreJournal);
where:
string xml = #"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:soapenc=""http://schemas.xmlsoap.org/soap/encoding/"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:dpss0=""bons"">
<soapenv:Header/>
<soapenv:Body>
<core:transactionResponse xmlns:bo=""http://service.example.co.id/core/bo"" xmlns:core=""http://service.example.co.id/core"">
<response>
<header>
<coreJournal>149326</coreJournal>
</header>
<content xsi:type=""bo:OKMessage"">
<message/>
</content>
</response>
</core:transactionResponse>
</soapenv:Body></soapenv:Envelope>";
which just has a null for .Response. The reason for that is that this is actually in the empty namespace (prefixes are not inherited), so you need
[XmlElement(ElementName = "response", Namespace = "")]
public Response Response { get; set; }
However, this then causes a problem with the OKMessage in the xml. If I comment out the <content>, then it works and I can see the output 149326.
However, it might be simpler to start by just copying the xml into the clipboard, Edit -> Paste Special -> Paste XML As Classes, and see what it generates, and work from there.
I haven't had much experience working with XML so forgive me if this is basic stuff. I have a feed that I'm trying to deserialize into an object(s) and some of it work but some doesn't. Here is a sample of the XML in question
<rss version ='2.0' xmlns:n='http://something.com/1.0'>
<group>
<title>Sample data</title>
<description>Sample feed description</description>
<link>https://www.sampledealership.co.uk/index.php/</link>
<item>
<n:id>xxx567</n:id>
<title>Ford Fiesta</title>
<description>A black Ford Fiesta</description>
</item>
<item>
<n:id>gg567</n:id>
<title>2019 Camero</title>
<description>A red Chevrolet Camero</description>
</item>
</group>
</rss>
Here are my classes
public class rss
{
[XmlAttribute(AttributeName = "version", Namespace = "")]
public decimal version { get; set; }
[XmlElement(ElementName = "group", Namespace = "")]
public Group group { get; set; }
}
public class Group
{
[XmlElement(ElementName = "title", Namespace = "")]
public string title { get; set; }
[XmlElement(ElementName = "description", Namespace = "")]
public string description { get; set; }
[XmlElement(ElementName = "link", Namespace = "")]
public string link { get; set; }
//[XmlArray("group")]
//[XmlArrayItem("item", typeof(Item))]
[XmlElement(ElementName = "item", Namespace = "")]
private List<Item> item { get; set; }
}
public class Item
{
[XmlElement(ElementName = "id", Namespace = "http://something.com/1.0")]
public string id { get; set; }
[XmlElement(ElementName = "title", Namespace = "")]
public string title { get; set; }
[XmlElement(ElementName = "description", Namespace = "")]
public string description { get; set; }
}
And here is the code in my test that is trying to deserialize it.
using (var stream = System.IO.File.OpenRead(#"Resources/carfeed.xml"))
{
var root = new XmlRootAttribute();
root.ElementName = "rss";
var serializer = new XmlSerializer(typeof(rss), root);
var car = ((rss) serializer.Deserialize(stream));
};
I get all the data contained in the group except for the Items, they are null. I have tried using the XmlArray annotation but still nothing. I'm hoping I have made a spelling mistake somewhere. Anyway, your help would be much appreciated.
I am having a very hard time trying to deserialize the soap response below.
I assume its because of the multiple namespaces or maybe because of the complex type (Serialization Array)
Soapformatter throws an Object reference exception and other more manual deserializations are returning an empty object. At this point I can only assume that I am not tagging my object correctly. What is the correct way to build out the BatchResponse object below so that it can deserialize from this response?
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">https://example.com/operations/fetch/BatchResponse</a:Action>
</s:Header>
<s:Body>
<BatchResponse xmlns="https://example.com/operations">
<BatchResult xmlns:b="https://example.com/responses" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<b:FailureMessages xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/Arrays" i:nil="true" />
<b:FailureType i:nil="true" />
<b:ProcessedSuccessfully>true</b:ProcessedSuccessfully>
<b:SessionId>1961810</b:SessionId>
<b:TotalPages>38</b:TotalPages>
</BatchResult>
</BatchResponse>
</s:Body>
</s:Envelope>
Also assuming Soapformatter continues to throw exceptions - what is the correct way to "xpath" to the BatchResponse object so that I can extract and deserialize?
Thanks
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication131
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Envelope));
Envelope envelope = (Envelope)serializer.Deserialize(reader);
}
}
[XmlRoot(ElementName = "Envelope", Namespace = "http://www.w3.org/2003/05/soap-envelope")]
public class Envelope
{
[XmlElement(ElementName = "Header", Namespace = "http://www.w3.org/2003/05/soap-envelope")]
public Header header { get; set; }
[XmlElement(ElementName = "Body", Namespace = "http://www.w3.org/2003/05/soap-envelope")]
public Body body { get; set; }
}
[XmlRoot(ElementName = "Header", Namespace = "http://www.w3.org/2003/05/soap-envelope")]
public class Header
{
[XmlElement(ElementName = "Action", Namespace = "http://www.w3.org/2005/08/addressing")]
public Action action { get; set; }
}
[XmlRoot(ElementName = "Action", Namespace = "http://www.w3.org/2005/08/addressing")]
public class Action
{
[XmlAttribute(AttributeName = "mustUnderstand", Namespace = "http://www.w3.org/2003/05/soap-envelope")]
public int mustUnderstand { get; set;}
[XmlText]
public string value { get;set;}
}
[XmlRoot(ElementName = "Body", Namespace = "")]
public class Body
{
[XmlElement(ElementName = "BatchResponse", Namespace = "https://example.com/operations")]
public BatchResponse batchResponse { get; set; }
}
[XmlRoot(ElementName = "BatchResponse", Namespace = "https://example.com/operations")]
public class BatchResponse
{
[XmlElement(ElementName = "BatchResult", Namespace = "https://example.com/operations")]
public BatchResult batchResult { get; set; }
}
[XmlRoot(ElementName = "BatchResult", Namespace = "https://example.com/operations")]
public class BatchResult
{
[XmlElement(ElementName = "FailureMessages", Namespace = "https://example.com/responses")]
public string failureMessages { get; set; }
[XmlElement(ElementName = "FailureType", Namespace = "https://example.com/responses")]
public string failureType { get; set; }
[XmlElement(ElementName = "ProcessedSuccessfully", Namespace = "https://example.com/responses")]
public Boolean processedSuccessfully { get; set; }
[XmlElement(ElementName = "SessionId", Namespace = "https://example.com/responses")]
public string sessionId { get; set; }
[XmlElement(ElementName = "TotalPages", Namespace = "https://example.com/responses")]
public int totalPages { get; set; }
}
}
you can use local-name() to ignore the namespace in xpath
//*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='BatchResponse']
I'm still struggling with deserialization of XML containing arrays of items.
The response I want to deserialize:
<ns1:OperationResult xmlns:ns1="http://xxxx.com">
<done>false</done>
<errorEntities>
<elements>
<entityID>100014</entityID>
<entityType>GROUP</entityType>
<errors>
<errorCode>INVALID_DATA</errorCode>
<errorMessage>s: d3f62887-a2a3-4cde-8f8b-09812a7bd011ed8d385e-f4c4-4fae-9a4b-1ba405db54b6-MessageTemplate:{k2.constraints.numberFormat.length}|length:5|; </errorMessage>
</errors>
</elements>
</errorEntities>
</ns1:OperationResult>
And this is my corresponding class:
[XmlRootAttribute(Namespace = "http://xxxx.", IsNullable = false, ElementName = "OperationResult")]
public class GroupCreateUpdateResult
{
[XmlElement(ElementName = "done")]
public string done { get; set; }
[XmlElement(ElementName = "errorEntities")]
public ErrorEntities errorEntities { get; set; }
public bool hasErrors => done == "true" ? true : false;
}
[XmlRoot(ElementName = "errorEntities")]
public class ErrorEntities
{
[XmlElement(ElementName = "elements")]
public List<ErrorElements> elements { get; } = new List<ErrorElements>();
}
[XmlRoot(ElementName = "elements")]
public class ErrorElements
{
[XmlElement(ElementName = "entityId")]
public string entityId { get; set; }
[XmlElement(ElementName = "entityType")]
public string entityType { get; set; }
[XmlElement(ElementName = "errors")]
Errors errors { get; set; }
}
[XmlRoot(ElementName = "errors")]
public class Errors
{
[XmlElement(ElementName = "errorCode")]
public string errorCode { get; set; }
[XmlElement(ElementName = "errorMessage")]
public string errorMessage { get; set; }
}
I have already a method deserializing my responses. Actually I am struggling with this specific one. Alle others without arrays are working fine.
What I finally get is this:
Any advice is highly appreciated.
You have a few issues
1) The namespace in the xml and the classes have to be the same
2) The tags names in the classes are case sensitive so you have to make sure the spelling is correct (Upper/Lower Case)
3) The class object have to be public otherwise the tags are ignored.
4) Where there are no namespaces in XML (and parent has a namespace) you need the empty string for the namespaces
See corrected code below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(GroupCreateUpdateResult));
GroupCreateUpdateResult group = (GroupCreateUpdateResult)serializer.Deserialize(reader);
}
}
[XmlRootAttribute(Namespace = "http://com.f24.soap.fwi.schema", IsNullable = false, ElementName = "OperationResult")]
public class GroupCreateUpdateResult
{
[XmlElement(ElementName = "done", Namespace = "")]
public string done { get; set; }
[XmlElement(ElementName = "errorEntities", Namespace = "")]
public ErrorEntities errorEntities { get; set; }
//public bool hasErrors => done == "true" ? true : false;
}
[XmlRoot(ElementName = "errorEntities")]
public class ErrorEntities
{
[XmlElement(ElementName = "elements", Namespace = "")]
public List<ErrorElements> elements { get; set;}
}
[XmlRoot(ElementName = "elements")]
public class ErrorElements
{
[XmlElement(ElementName = "entityID")]
public string entityId { get; set; }
[XmlElement(ElementName = "entityType")]
public string entityType { get; set; }
[XmlElement(ElementName = "errors", Namespace = "")]
public Errors errors { get; set; }
}
[XmlRoot(ElementName = "errors")]
public class Errors
{
[XmlElement(ElementName = "errorCode")]
public string errorCode { get; set; }
[XmlElement(ElementName = "errorMessage")]
public string errorMessage { get; set; }
}
}
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.