I am attempting to create a POST function that serialises C# class objects into XML.
The part I am having great difficulty with is adding namespace prefixes to the sub-root element's children, so in this instance, contact children only.
The only way I seem to be able to get the prefix onto the child elements of contactis to add them through SerializerNamespace class, however I can only get this to attach to the root element, CreateContact.
How can I achieve this?
XML Currently Produced:
<?xml version=\"1.0\"?>
<CreateContact xmlns:a="http://foo.co.uk/Contact" xmlns="http://foo.co.uk">
<a:contact>
<a:Email>stest#gmail.com</a:Email>
<a:FirstName>Simon</a:FirstName>
<a:LastName>Test</a:LastName>
<a:Phone>09088408501</a:Phone>
<a:Title>Mr</a:Title>
</a:contact>
</CreateContact>
Serialisation function:
public static void CreateContact(Contact contact)
{
string tmp = url;
string xml = "";
string result = "";
XmlDocument xd = new XmlDocument();
var cc = new CreateContact();
cc.contact = contact;
var xs = new XmlSerializer(cc.GetType());
XmlSerializerNamespaces xsn = new XmlSerializerNamespaces();
xsn.Add("a", "http://foo.co.uk/Contact");
using (MemoryStream ms = new MemoryStream())
{
xs.Serialize(ms, cc, xsn);
ms.Position = 0;
xd.Load(ms);
xml = xd.InnerXml;
}
using (WebClient web = new WebClient())
{
web.Credentials = new NetworkCredential(username, password);
web.Headers.Add("Content-Type", "application/xml");
try
{
result = web.UploadString(tmp, "POST", xml);
}
catch (WebException ex)
{
}
}
}
XML Class Constructs:
[Serializable()]
[XmlRoot(ElementName = "CreateContact", Namespace = "http://foo.co.uk")]
public class CreateContact
{
[XmlElement(ElementName = "contact", Namespace = "http://foo.co.uk/Contact")]
public Contact contact { get; set; }
}
[DataContract(Name = "Contact", Namespace = "http://foo.co.uk/Contact")]
[XmlType("a")]
public class Contact
{
[XmlElement(ElementName = "Email", Namespace = "http://foo.co.uk/Contact")]
[DataMember(Name = "Email")]
public string Email { get; set; }
[XmlElement(ElementName = "FirstName", Namespace = "http://foo.co.uk/Contact")]
[DataMember(Name = "FirstName")]
public string Firstname { get; set; }
[XmlElement(ElementName = "LastName", Namespace = "http://foo.co.uk/Contact")]
[DataMember(Name = "LastName")]
public string Lastname { get; set; }
[XmlElement(ElementName = "Phone", Namespace = "http://foo.co.uk/Contact")]
[DataMember(Name = "Phone")]
public string Phone { get; set; }
[XmlElement(ElementName = "Title", Namespace = "http://foo.co.uk/Contact")]
[DataMember(Name = "Title")]
public string Title { get; set; }
}
XML Desired:
<?xml version=\"1.0\"?>
<CreateContact xmlns="http://foo.co.uk">
<contact xmlns:a="http://foo.co.uk/Contact">
<a:Email>stest#gmail.com</a:Email>
<a:FirstName>Simon</a:FirstName>
<a:LastName>Test</a:LastName>
<a:Phone>09088408501</a:Phone>
<a:Title>Mr</a:Title>
</contact>
</CreateContact>
As alluded in the comments, the reason for the difference is that contact should be in the namespace http://foo.co.uk, not http://foo.co.uk/Contact.
As an aside, a couple of further comments:
You probably don't need the DataMember attributes, unless you're using DataContractSerializer somewhere else.
Most of the Xml* attributes are superfluous here, and could be removed or consolidated by inheriting from XmlRoot.
If all you need is the XML string, you'd be better off serialising to something like a StringWriter rather than to a stream and then loading into the DOM just to get the text (see this question if you need the XML declaration to specify utf-8)
So, you'd get the XML as below:
var xsn = new XmlSerializerNamespaces();
xsn.Add("a", "http://foo.co.uk/Contact");
var xs = new XmlSerializer(typeof(CreateContact));
using (var stringWriter = new StringWriter())
{
xs.Serialize(stringWriter, cc, xsn);
xml = stringWriter.ToString();
}
With your classes defined as:
[XmlRoot(ElementName = "CreateContact", Namespace = "http://foo.co.uk")]
public class CreateContact
{
[XmlElement(ElementName = "contact")]
public Contact Contact { get; set; }
}
[XmlRoot("contact", Namespace = "http://foo.co.uk/Contact")]
public class Contact
{
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone { get; set; }
public string Title { get; set; }
}
See this fiddle for the complete example.
Related
I am using XML serialization in my project. It's working good but I am facing two issues.
Formatting of nodes
Remove <string></string> from response
Sample object which I am trying to serialize is :
[XmlRoot("result")]
public class SwRequestListModel
{
public SwRequestListModel()
{
SwRequest = new List<SwRequestShortInfo>();
}
[XmlElement("api_version")]
public string ApiVersion { get; set; }
[XmlElement("sw_request")]
public List<SwRequestShortInfo> SwRequest { get; set; }
}
[XmlRoot(ElementName = "sw_request_short_info")]
public class SwRequestShortInfo
{
[XmlElement(ElementName = "sw_ref_number")]
public string SwRefNumber { get; set; }
[XmlElement(ElementName = "db_no")]
public string DbNo { get; set; }
[XmlElement(ElementName = "engine_manufacturing_no")]
public string EngineManufacturingNo { get; set; }
[XmlElement(ElementName = "engine_ref_type")]
public string EngineRefType { get; set; }
[XmlElement(ElementName = "raised_date")]
public string RaisedDate { get; set; }
[XmlElement(ElementName = "raised_by")]
public string RaisedBy { get; set; }
[XmlElement(ElementName = "status")]
public string Status { get; set; }
[XmlElement(ElementName = "approved_by")]
public string ApprovedBy { get; set; }
[XmlElement(ElementName = "system_type")]
public string SystemType { get; set; }
}
The code which I am using to serialize is :
public static string ToXml(SwRequestListModel obj)
{
XmlSerializer s = new XmlSerializer(obj.GetType());
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = true,
Encoding = Encoding.UTF8,
NewLineOnAttributes = true,
NamespaceHandling = NamespaceHandling.OmitDuplicates,
IndentChars = Environment.NewLine
};
using (var sww = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sww, settings))
{
s.Serialize(writer, obj, ns);
return sww.ToString();
}
}
}
Response is display like :
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/"><result> <api_version>1.0</api_version> <sw_request> <sw_ref_number>588</sw_ref_number> <db_no>99899</db_no> <engine_manufacturing_no>MF99899</engine_manufacturing_no> <engine_ref_type>X40B</engine_ref_type> <raised_date>22/06/2021</raised_date> <raised_by>Srusti.Thakkar#test.com</raised_by> <status>Requested</status> <approved_by /> <system_type>test</system_type> </sw_request> <sw_request> <sw_ref_number>589</sw_ref_number> <db_no>88552</db_no> <engine_manufacturing_no>MF99899</engine_manufacturing_no> <engine_ref_type>X40B</engine_ref_type> <raised_date>22/06/2021</raised_date> <raised_by>Srusti.Thakkar#test.com</raised_by> <status>Requested</status> <approved_by /> <system_type>UNIC</system_type> </sw_request> <sw_request> <sw_ref_number>590</sw_ref_number> <db_no>33899</db_no> <engine_manufacturing_no>MF99899</engine_manufacturing_no> <engine_ref_type>X40B</engine_ref_type> <raised_date>22/06/2021</raised_date> <raised_by>Srusti.Thakkar#test.com</raised_by> <status>Requested</status> <approved_by /> <system_type>UNIC</system_type> </sw_request> </result></string>
Actual API Method is :
[HttpGet]
public string GetRequestList(string date, string status, string systemType)
{
SwRequestListModel result = new SwRequestListModel();
result.ApiVersion = "1.0";
//Here get data
return result.ToXml();
}
Browser result is :
Update :
I guess API method GetRequestList serialize string object. Try to return raw string.
[HttpGet]
public HttpResponseMessage GetRequestList(string date, string status, string systemType)
{
SwRequestListModel result = new SwRequestListModel();
result.ApiVersion = "1.0";
//Here get data
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(result.ToXml());
return response;
}
When i try to parse soap xml to objects, i'm getting below exception not sure what i'm doing wrong.
Exception:
An unhandled exception of type 'System.Runtime.Serialization.SerializationException' occurred in System.Runtime.Serialization.dll
Additional information: Error in line 1 position 687. Element 'http://soap.sforce.com/2005/09/outbound:sObject' contains data from a type that maps to the name 'urn:sobject.enterprise.soap.sforce.com:Contact'. The deserializer has no knowledge of any type that maps to this name. Consider using a DataContractResolver if you are using DataContractSerializer or add the type corresponding to 'Contact' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to the serializer.
static void Main(string[] args)
{
string inputString = "<?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\"> <SessionId xsi:nil=\"true\"/> <EnterpriseUrl>https://hultibs--FullDev.cs10.my.salesforce.com/services/Soap/c/44.0/00DJ0000003QX7f</EnterpriseUrl> <PartnerUrl>https://hultibs--FullDev.cs10.my.salesforce.com/services/Soap/u/44.0/00DJ0000003QX7f</PartnerUrl> <Notification> <Id>04lJ000000PoRS2IAN</Id> <sObject xsi:type=\"sf:Contact\" xmlns:sf=\"urn:sobject.enterprise.soap.sforce.com\"> <sf:Id>0033600001koT9qAAE</sf:Id> <sf:Email>tcampbell2018#maili.com</sf:Email> <sf:Student_ID__c>5192435</sf:Student_ID__c> </sObject> </Notification> </notifications> </soapenv:Body> </soapenv:Envelope>";
FromXml(inputString);
Console.ReadLine();
}
public static void FromXml(string Xml)
{
using (var reader = XmlReader.Create(new StringReader(Xml)))
{
Message m = Message.CreateMessage(reader, int.MaxValue, MessageVersion.Soap11);
var body = m.GetBody<Notifications>();
Console.WriteLine(body);
}
}
[DataContract(Name = "sObject", Namespace = "http://soap.sforce.com/2005/09/outbound")]
public class SObject
{
[DataMember(Name = "Id", Order = 1)]
public string Id { get; set; }
[DataMember(Name = "Email", Order = 2)]
public string Email { get; set; }
[DataMember(Name = "Student_ID__c", Order = 3)]
public string Student_ID__c { get; set; }
[DataMember(Name = "type", Order = 4)]
public string Type { get; set; }
[DataMember(Name = "sf", Order = 5)]
public string Sf { get; set; }
}
[DataContract(Name = "Notification", Namespace = "http://soap.sforce.com/2005/09/outbound")]
public class Notification
{
[DataMember(Name = "Id", Order = 1)]
public string Id { get; set; }
[DataMember(Name = "sObject", Order = 2)]
public SObject SObject { get; set; }
}
[DataContract(Name = "notifications", Namespace = "http://soap.sforce.com/2005/09/outbound")]
public class Notifications
{
[DataMember(Name = "ActionId", Order = 2)]
public string ActionId { get; set; }
[DataMember(Name = "EnterpriseUrl", Order = 3)]
public string EnterpriseUrl { get; set; }
[DataMember(Name = "PartnerUrl", Order = 4)]
public string PartnerUrl { get; set; }
[DataMember(Name = "Notification", Order = 5)]
public Notification Notification { get; set; }
}
The problem, which is described in the exception, is in the following type and namespace declaration for sObject in the soap message
<sObject xsi:type=\"sf:Contact\" xmlns:sf=\"urn:sobject.enterprise.soap.sforce.com\">
because there is no class Contact defined in that namespace (or any other).
If you remove the type and namespace declaration from sObject in the soap message (and remove the sf: prefix from its members) it should work OK.
Or remove the xsi:type=\"sf:Contact\ and change the DataContract to
[DataContract(Name = "sObject", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
Or leave the soap message as it is and change
[DataContract(Name = "sObject", Namespace = "http://soap.sforce.com/2005/09/outbound")]
public class SObject
to
[DataContract(Name = "Contact", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
public class Contact
also changing (in Notification)
[DataMember(Name = "sObject", Order = 2)]
public SObject SObject { get; set; }
to
[DataMember(Name = "sObject", Order = 2)]
public Contact SObject { get; set; }
You only declare a namespace "http://soap.sforce.com/2005/09/outbound" in your DataContract, you could use Message.CreateMessage to serialize your Notifications and compare your xml with the serialized message.
Below is the code.
static void Main(string[] args)
{
Notifications notifications = new Notifications()
{
ActionId = "actionId",
EnterpriseUrl = "enterpriceUri",
PartnerUrl = "parentUri",
Notification = new Notification
{
Id = "abc",
SObject = new SObject
{
Email = "email",
Id = "id",
Sf = "sf",
Student_ID__c = "a",
Type = "type"
}
}
};
Message me = Message.CreateMessage(MessageVersion.Soap11, "www.abc.com", notifications); // create a message and serialize the notifications into the message
WriteMessage(me, #"d:\message.xml");
}
static void WriteMessage(Message message, string fileName)
{
using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))
{
message.WriteMessage(writer);// write the message into a file
}
Process.Start(fileName);// show the file
}
And the serialized messsage.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Header><Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">www.abc.com</Action></s:Header><s:Body><notifications xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://soap.sforce.com/2005/09/outbound"><ActionId>actionId</ActionId><EnterpriseUrl>enterpriceUri</EnterpriseUrl><PartnerUrl>parentUri</PartnerUrl><Notification><Id>abc</Id><sObject><Id>id</Id><Email>email</Email><Student_ID__c>a</Student_ID__c><type>type</type><sf>sf</sf></sObject></Notification></notifications></s:Body></s:Envelope>
I'm trying to create a XML something like this :
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
- <inventory_report:inventoryReportMessage xmlns:inventory_report="urn:gs1:ecom:inventory_report:xsd:3" xmlns:sh="http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader" xmlns:ecom_common="urn:gs1:ecom:ecom_common:xsd:3" xmlns:shared_common="urn:gs1:shared:shared_common:xsd:3">
- <sh:StandardBusinessDocumentHeader>
<sh:HeaderVersion>Standard Business Header version 1.3</sh:HeaderVersion>
- <sh:Sender>
<sh:Identifier Authority="GS1">0000</sh:Identifier>
- <sh:ContactInformation>
<sh:Contact>some one</sh:Contact>
<sh:EmailAddress>someone#example.com</sh:EmailAddress>
<sh:TelephoneNumber>00357</sh:TelephoneNumber>
<sh:ContactTypeIdentifier>IT Support</sh:ContactTypeIdentifier>
</sh:ContactInformation>
</sh:Sender>
I'm using the below code for creating the XML -->
var xelementNode = doc.CreateElement("inventory_report", "inventoryReportMessage","urn:gs1:ecom:inventory_report:xsd:3");
doc.AppendChild(xelementNode);
var xelementSubNode = doc.CreateElement("sh", xelementNode, "StandardBusinessDocumentHeades","");
xelementNode.AppendChild(xelementSubNode);
I'm getting this output for the above code -->
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes" ?>
- <inventory_report:inventoryReportMessage xmlns:inventory_report="urn:gs1:ecom:inventory_report:xsd:3">
- <StandartBusinessDocumentHeader>
<HeaderVersion>Standard Business Header Version 1.3</HeaderVersion>
- <Sender>
<Identifier>GS1</Identifier>
- <ContactInformation>
<Contact>Turkey IT Support</Contact>
<EmailAddress>someone#example.com</EmailAddress>
<TelephoneNumber>00 13</TelephoneNumber>
<ContactTypeIdentifier>IT Support</ContactTypeIdentifier>
</ContactInformation>
</Sender>
</StandartBusinessDocumentHeader>
</inventory_report:inventoryReportMessage>
The second prefix ("sh") doesn't work. Can someone help me???
For serialization approach, you can define classes:
public class ContactInformation
{
[XmlElement(ElementName = "Contact")]
public string Contact { get; set; }
[XmlElement(ElementName = "EmailAddress")]
public string EmailAddress { get; set; }
[XmlElement(ElementName = "TelephoneNumber")]
public string TelephoneNumber { get; set; }
[XmlElement(ElementName = "ContactTypeIdentifier")]
public string ContactTypeIdentifier { get; set; }
}
public class Identifier
{
[XmlAttribute("Authority")]
public string Authority { get; set; }
[XmlText]
public string Value { get; set; }
}
public class Sender
{
[XmlElement(ElementName = "Identifier")]
public Identifier Identifier { get; set; }
[XmlElement(ElementName = "ContactInformation")]
public ContactInformation ContactInformation { get; set; }
}
public class StandartBusinessDocumentHeader
{
[XmlElement(ElementName = "HeaderVersion")]
public string HeaderVersion { get; set; }
[XmlElement(ElementName = "Sender")]
public Sender Sender { get; set; }
}
[XmlRoot(ElementName = "inventoryReportMessage", Namespace = "urn:gs1:ecom:inventory_report:xsd:3")]
public class InventoryReportMessage
{
[XmlElement("StandardBusinessDocumentHeader", Namespace = "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader")]
public StandartBusinessDocumentHeader Header { get; set; }
}
then serialize those as below:
var report = new InventoryReportMessage
{
Header = new StandartBusinessDocumentHeader {
HeaderVersion = "Standard Business Header version 1.3",
Sender = new Sender
{
Identifier = new Identifier
{
Authority = "GS1",
Value = "0000"
},
ContactInformation = new ContactInformation
{
Contact = "some one",
EmailAddress = "someone#example.com",
TelephoneNumber = "00357",
ContactTypeIdentifier = "IT Support"
}
}
}
};
using (var stream = new MemoryStream())
{
using (var writer = new StreamWriter(stream))
{
var settings = new XmlWriterSettings {
Indent = true
};
using (var xmlWriter = XmlWriter.Create(writer, settings))
{
xmlWriter.WriteStartDocument(false);
var serializer = new XmlSerializer(typeof(InventoryReportMessage));
var namespaces = new XmlSerializerNamespaces();
namespaces.Add("inventory_report", "urn:gs1:ecom:inventory_report:xsd:3");
namespaces.Add("sh", "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader");
namespaces.Add("ecom_common", "urn:gs1:ecom:ecom_common:xsd:3");
namespaces.Add("shared_common", "urn:gs1:shared:shared_common:xsd:3");
serializer.Serialize(xmlWriter, report, namespaces);
}
stream.Position = 0;
using (var reader = new StreamReader(stream))
{
Console.WriteLine(reader.ReadToEnd());
}
}
}
Console.ReadLine();
Using xml linq. I like to create a string header and then add dynamic values in code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string header =
"<inventory_report:inventoryReportMessage xmlns:inventory_report=\"urn:gs1:ecom:inventory_report:xsd:3\" xmlns:sh=\"http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader\" xmlns:ecom_common=\"urn:gs1:ecom:ecom_common:xsd:3\" xmlns:shared_common=\"urn:gs1:shared:shared_common:xsd:3\">" +
"<sh:StandardBusinessDocumentHeader>" +
"<sh:HeaderVersion>Standard Business Header version 1.3</sh:HeaderVersion>" +
"<sh:Sender>" +
"</sh:Sender>" +
"</sh:StandardBusinessDocumentHeader>" +
"</inventory_report:inventoryReportMessage>";
XDocument doc = XDocument.Parse(header);
XElement sender = doc.Descendants().Where(x => x.Name.LocalName == "Sender").FirstOrDefault();
XNamespace shNs = sender.GetNamespaceOfPrefix("sh");
sender.Add(new XElement(shNs + "Identifier", new object[] {
new XAttribute("Authority", "GS1"),
"0000"
}));
sender.Add( new XElement(shNs + "ContactInformation", new object[] {
new XElement(shNs + "Contact", "some one"),
new XElement(shNs + "EmailAddress", "someone#example.com"),
new XElement(shNs + "TelephoneNumber", "00357"),
new XElement(shNs + "ContactTypeOdemtofier", "IT Support")
}));
}
}
}
[XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.example.org/schema/SCRIPT")]
public class Identification
{
public string DEANumber { get; set; }
public uint NPI { get; set; }
}
<someprefix:Identification>
<someprefix:DEANumber>FF1234567</DEANumber>
<someprefix:NPI>1619967999</NPI>
</someprefix:Identification>
How to assign namespace prefix to class elements
Use a XmlSerializerNamespaces as such :
var id = new Identification()
{
DEANumber = "qwe",
NPI = 123,
};
var serializer = new XmlSerializer(typeof(Identification));
var xmlns = new XmlSerializerNamespaces();
xmlns.Add("someprefix", "http://www.example.org/schema/SCRIPT");
serializer.Serialize(Console.Out, id, xmlns);
I am serializing the following entity into XML to send to our Google Search Appliance:
[Serializable]
[XmlType("record")]
public class GSADocumentRecord
{
public enum RecordActions
{
Add,
Delete
}
[XmlAttribute(AttributeName = "url")]
public string URL { get; set; }
[XmlAttribute(AttributeName = "mimetype")]
public string MimeType { get; set; }
[XmlAttribute(AttributeName = "last-modified")]
public string LastModified { get; set; }
[XmlAttribute(AttributeName = "action")]
public string Action { get; set; }
[XmlArray(ElementName = "metadata", Order = 0)]
public List<GSADocumentRecordMeta> MetaData { get; set; }
[XmlElement(ElementName = "content", Order = 1, Type = typeof(CDATA))]
public CDATA Content { get; set; }
}
The problem is that when this is serialzied without any MetaData entries, it adds <metadata /> to the xml. This is a problem because GSA (for whatever reason) errors out if there is an empty metadata node when used for some actions.
I am serializing this class with the following code:
var ms = new System.IO.MemoryStream();
XmlSerializer xml = new XmlSerializer(this.GetType());
StreamWriter sw = new StreamWriter(ms);
XmlWriter xw = new XmlTextWriter(sw);
xw.WriteStartDocument();
xw.WriteDocType("gsafeed", "-//Google//DTD GSA Feeds//EN", null, null);
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
xml.Serialize(xw, this, ns);
ms.Position = 0;
How can I tell the XmlWriter to ignore this element if the list is empty?
Having a self-closing tag certainly seems legal, it sounds like the parser on their side is causing the problem. You could write the XML out to a string first, and then do a .Replace("<metadata />", "").