Serialize a list of primitives to XML with overrides - c#

I'm trying to serialize an array of integers to XML, but I can't quite get the output to look the way I want. I need to find some way to override the element name of each element in the array.
The code I have so far:
var numbers = new int[]{1, 2, 3, 4, 5};
var serializer = new XmlSerializer(typeof(int[]), new XmlRootAttribute("rows"));
using (var stringWriter = new StringWriter())
{
serializer.Serialize(stringWriter, numbers);
Console.Write(stringWriter.ToString());
}
The output I am after:
<?xml version="1.0" encoding="utf-16"?>
<rows xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<row>1</row>
<row>2</row>
<row>3</row>
<row>4</row>
<row>5</row>
</rows>
The current output that I see:
<?xml version="1.0" encoding="utf-16"?>
<rows xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<int>1</int>
<int>2</int>
<int>3</int>
<int>4</int>
<int>5</int>
</rows>

You can create a seperate class to hold your values and you can specify Xml attributes as follows:
[Serializable]
[XmlRoot("rows")]
public class Rows
{
[XmlElement("row")]
public List<int> Elements { get; set; }
}
public static void SerializeOnScreen()
{
Rows numbers = new Rows();
numbers.Elements = new List<int>() { 1, 2, 3, 4, 5 };
var serializer = new XmlSerializer(typeof(Rows));
using (var stringWriter = new StringWriter())
{
serializer.Serialize(stringWriter, numbers);
Console.Write(stringWriter.ToString());
}
}

Related

c# Generate XML document based on Xpath

I need to create an XML document like this:
<Root>
<Data>
<Name>Name1</Name>
<Surname>Surname1</Surname>
<Gender>M</Gender>
</Data>
<Data>
<Name>Name2</Name>
<Surname>Surname2</Surname>
<Gender>F</Gender>
</Data>
</Root>
I've got the Xpath of the XML Elements, so I've create the following class
class XpathFieldValue
{
public string Xpath { get; set; }
public string Value { get; set; }
}
and then the following method
public static void CreateXml()
{
List<XpathFieldValue> fieldValues = new List<XpathFieldValue> {
new XpathFieldValue{ Xpath="/Root/Data/Name", Value="Name1" },
new XpathFieldValue{ Xpath="/Root/Data/Surname", Value="Surname1" },
new XpathFieldValue{ Xpath="/Root/Data/Gender", Value="M"},
new XpathFieldValue{ Xpath="/Root/Data/Name", Value="Name2" },
new XpathFieldValue{ Xpath="/Root/Data/Surname", Value="Surname2" },
new XpathFieldValue{ Xpath="/Root/Data/Gender", Value="F"}
};
XmlDocument document = new XmlDocument();
document.LoadXml("<Root/>");
foreach (XpathFieldValue fieldValue in fieldValues)
{
Set(document, fieldValue.Xpath, fieldValue.Value);
}
document.Save(#"C:\Temp\xmlDocOut.xml");
}
I've copyed the Set method from here: link
But when I run it it creates only the last parth of XML
<Root>
<Data>
<Name>Name2</Name>
<Surname>Surname2</Surname>
<Gender>F</Gender>
</Data>
</Root>
Can anyone help me?
XPath isn't really intended to generate documents but to access em.
Another (probably better) approach would be to serialize it.
But, since the question requires the fields of xml to be variable, serialization isn't a viable way. i STRONGLY advise you to change this requirement and you'll see why in the following example:
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
using (var writer = new System.IO.StreamWriter(#"C:\Users\luigi.trabacchin\Desktop\asd.xml"))
{
var doc = new System.Xml.XmlDocument();
var root = doc.CreateElement("Root");
doc.AppendChild(root);
for (var i = 0; i <= 1; i++)
{
var dataNode = doc.CreateElement("Data");
root.AppendChild(dataNode);
{
var node = doc.CreateElement("Name");
dataNode.AppendChild(node);
var text = doc.CreateTextNode($"Name {i}");
node.AppendChild(text);
}
{
var node = doc.CreateElement("Surname");
dataNode.AppendChild(node);
var text = doc.CreateTextNode($"Surname {i}");
node.AppendChild(text);
}
{
var node = doc.CreateElement("Gender");
dataNode.AppendChild(node);
var text = doc.CreateTextNode(i %2 == 0 ? "M" : "F");
node.AppendChild(text);
}
}
doc.Save(writer);
}
Console.WriteLine("Hello World!");
}
}
}
It quickly becomes very tedious and hard to work with.
Still i hope this answer your question.
Next time, maybe, state all the requirements directly in the question!

How would I parse an XML file into a list

List:
List<string> list = new List<string>();
XML File:
<memberlist>
<member>
<name>Name</name>
<status>Status</status>
</member>
</memberlist>
How would I go about parsing this file, lets say (file.xml) into the list?
I've tried many ways but none of them seem to be working.
I want to check to see if the status is 'gold', and if it is, I would like to put the name of that member into the list.
Load the xml into an XDocument. You can do this from a file, I have demonstrated from a string.
Do a linq query for elements named "status" (within "member") that also have a value of "gold".
Of those, grab the value of the "name" element.
Use AddRange on your list of strings.
List<string> lstGolds = new List<string>();
string xml ="<memberlist><member><name>Name</name><status>gold</status></member></memberlist>";
XDocument doc = XDocument.Parse(xml);
var goldStatus = doc.Descendants("member")
.Where(d => d.Element("status").Value == "gold")
.Select(d => d.Element("name").Value);
lstGolds.AddRange(goldStatus);
You should use using System.Xml.Linq;
using (FileStream fileStramWrite = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
using (StreamReader streamReader = new StreamReader(fileStramWrite))
{
var xdoc = XDocument.Load(streamReader);
var xElement = xdoc.Element("memberlist");
var xElement2 = xElement.Element("member");
var name = xElement2.Element("name").Value;
var status = xElement2.Element("status").Value;
}
Convert your XML to a list of objects....
Use these two mehods:
public static string Serialize<T>(T dataToSerialize)
{
try
{
var stringwriter = new ISOEncodingStringWriter();
var serializer = new XmlSerializer(typeof(T));
var xns = new XmlSerializerNamespaces();
xns.Add(string.Empty, string.Empty);
serializer.Serialize(stringwriter, dataToSerialize, xns);
return stringwriter.ToString();
}
catch
{
throw;
}
}
public static T Deserialize<T>(string xmlText)
{
try
{
var stringReader = new System.IO.StringReader(xmlText);
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
catch
{
throw;
}
}
Make the following Class where you give the following attributes
[XmlRoot(ElementName = "Members", Namespace = "")]
public class Members
{
[XmlElement("Member")]
public Member[] member { get; set; }
}
public class Member
{
[XmlElementAttribute("Name")]
public string Name { get; set; }
[XmlElementAttribute("Status")]
public string Status { get; set; }
}
Then deserialize your data.
Members result = Deserialize<Members>(XML_String);
You got your members in a array and you ca convert that to a list if you want.
You can use LINQ
XElement xml = XElement.Load("locationToYourXmlFile");
var members = xml.Elements("member");
var list = members.Where(member => member.Element("status")?.Value == "Gold")
.Select(member => member.Element("name")?.Value)
.ToList();
Assuming your XML has a bunch of member elements under the root memberlist element, each with a name and status element inside that.
<?xml version="1.0" encoding="utf-8"?>
<memberlist>
<member>
<name>John</name>
<status>Gold</status>
</member>
<member>
<name>Sam</name>
<status>Silver</status>
</member>
<member>
<name>Sara</name>
<status>Gold</status>
</member>
</memberlist>

XML Parse to list

[XmlRoot("Employees")]
public class Employee
{
[XmlElement("EmpId")]
public int Id { get; set; }
[XmlElement("Name")]
public string Name { get; set; }
}
and simple method, which return List:
public static List<Employee> SampleData()
{
return new List<Employee>()
{
new Employee(){
Id = 1,
Name = "pierwszy"
},
new Employee(){
Id = 2,
Name = "drugi"
},
new Employee(){
Id = 3,
Name = "trzeci"
}
};
}
Program.cs:
var list = Employee.SampleData();
XmlSerializer ser = new XmlSerializer(typeof(List<Employee>));
TextWriter writer = new StreamWriter("nowi.xml");
ser.Serialize(writer, list);
I have file result:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfEmployee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Employee>
<EmpId>1</EmpId>
<Name>pierwszy</Name>
</Employee>
<Employee>
<EmpId>2</EmpId>
<Name>drugi</Name>
</Employee>
<Employee>
<EmpId>3</EmpId>
<Name>trzeci</Name>
</Employee>
</ArrayOfEmployee>
but i would like for Root Element has name: "Employees", not "ArrayOfEmployee"
how can i make it?
I want to do it, because i have file, where structure looks like:
<Employees>
<Employee>
...
</Employee>
<Employee>
...
</Employee>
</Employees>
Just change as below
XmlSerializer ser = new XmlSerializer(typeof(List<Employee>),
new XmlRootAttribute("Employees"));
that's all. But to get a clean xml as in your question (no xml declaration, no xsi or xsd namespaces etc.), you should use a few tricks
XmlSerializer ser = new XmlSerializer(typeof(List<Employee>),
new XmlRootAttribute("Employees"));
TextWriter writer = new StreamWriter(filename);
var xmlWriter = XmlWriter.Create(writer, new XmlWriterSettings() { OmitXmlDeclaration = true, Indent = true });
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
ser.Serialize(xmlWriter, list, ns);
You can pass the XmlRootAttribute to set the element Name:
var root = new XmlRootAttribute("Employees");
XmlSerializer ser = new XmlSerializer(typeof(List<Employee>), root);
TextWriter writer = new StreamWriter("nowi.xml");
ser.Serialize(writer, list);
From http://msdn.microsoft.com/en-us/library/f1wczcys%28v=vs.110%29.aspx :
... the root parameter allows you to replace the default object's
information by specifying an XmlRootAttribute; the object allows you
to set a different namespace, element name, and so on.
You can mark your property with attributes, use the XmlArray and XmlArrayItem attribute

Deserialize to list

i am using the XmlSerializer.Serialize and XmlSerializer.Deserialize
i have a class MetricResult:
public class Metricresult
{
public string metric;
public string newvalue;
public string deltavalue;
}
and i am calculating some results that will be stored in :
List<Metricresult> metricresults = new List<Metricresult>();
i changed this list to xml file succefully and in an other project i need to deserialize this list but there is an exception thrown :
there-is-an-error-in-xml-document-0-0-during-deserialization
the xml File
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMetricresult xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Metricresult>
<metric>numberofdeclaration</metric>
<newvalue>0</newvalue>
<deltavalue>0</deltavalue>
</Metricresult>
<Metricresult>
<metric>numberofdeclaration</metric>
<newvalue>0</newvalue>
<deltavalue>0</deltavalue>
</Metricresult>
<Metricresult>
<metric>numberofdeclaration</metric>
<newvalue>0</newvalue>
<deltavalue>0</deltavalue>
</Metricresult>
<Metricresult>
<metric>numberofdeclaration</metric>
<newvalue>0</newvalue>
<deltavalue>0</deltavalue>
</Metricresult>
<Metricresult>
<metric>numberofdeclaration</metric>
<newvalue>0</newvalue>
<deltavalue>0</deltavalue>
</Metricresult>
<Metricresult>
<metric>numberofdeclaration</metric>
<newvalue>0</newvalue>
<deltavalue>0</deltavalue>
</Metricresult>
</ArrayOfMetricresult>
Weird, coz the following works perfectly fine for me:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
public class MetricResult
{
public string Metric { get; set; }
public string NewValue { get; set; }
public string DeltaValue { get; set; }
}
class Program
{
static void Main()
{
// define some metric results to serialize
var metricResults = new[]
{
new MetricResult { Metric = "metric 1", NewValue = "new value 1", DeltaValue = "delta 1" },
new MetricResult { Metric = "metric 2", NewValue = "new value 2", DeltaValue = "delta 2" },
new MetricResult { Metric = "metric 3", NewValue = "new value 3", DeltaValue = "delta 3" },
}.ToList();
var serializer = new XmlSerializer(metricResults.GetType());
using (var writer = XmlWriter.Create("metrics.xml"))
{
// serialize the metric results we defined previously to metrics.xml
serializer.Serialize(writer, metricResults);
}
using (var reader = XmlReader.Create("metrics.xml"))
{
// read metrics.xml and deserialize back
metricResults = (List<MetricResult>)serializer.Deserialize(reader);
foreach (var result in metricResults)
{
Console.WriteLine("metric: {0}, new value: {1}, delta: {2}", result.Metric, result.NewValue, result.DeltaValue);
}
}
}
}
and metrics.xml looks as expected:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMetricResult xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MetricResult>
<Metric>metric 1</Metric>
<NewValue>new value 1</NewValue>
<DeltaValue>delta 1</DeltaValue>
</MetricResult>
<MetricResult>
<Metric>metric 2</Metric>
<NewValue>new value 2</NewValue>
<DeltaValue>delta 2</DeltaValue>
</MetricResult>
<MetricResult>
<Metric>metric 3</Metric>
<NewValue>new value 3</NewValue>
<DeltaValue>delta 3</DeltaValue>
</MetricResult>
</ArrayOfMetricResult>
An error at 0,0 usually means you have whitespace at the start of your xml file, before the first element. Look carefully at the xml (perhaps looking at the binary, rather than the text - it could also be a unicode invisible space, etc).
Also: whenever using XmlSerializer, look at the InnerExceptions:
catch(Exception ex) {
while(ex != null) {
Trace.WriteLine(ex.Message);
ex = ex.InnerException;
}
}
there are usually very detailed explanations, if you look all the way through the messages.
One very common error people make is when using streams, not rewinding them. The following will error with a 0,0 error:
using (var ms = new MemoryStream())
{
serializer.Serialize(ms, metricResults);
serializer.Deserialize(ms);
}
because the serialize left the stream at the end. If you have code like this, reset the stream to the start (ms.Position = 0;) between serialize and deserialize. See also: How many ways can you mess up IO?

Pass parameter as an object[]

I wish to use this API with a c# application:
http://www.affjet.com/2012/11/26/4-4-affjet-api/#more-3099
After adding the wsdl to my projcet i wrote this simple code :
(getTransactions gets a object[] #params and returns a string)
Ws_ApiService service = new Ws_ApiService();
string apiKey = "*************";
var response = service.getTransactions(new object[] { apiKey });
i tried few more ways but couldnt get a right response,
i tried :
var response = service.getTransactions(new object[] { "apiKey:****"});
and
var response = service.getTransactions(new object[] { "apiKey","******"});
Here is the php code that does the same from their docs :
<?php
$nameSpace = "https://secure.affjet.com/ws/api";
//Creating AffJet client for SOAP
$client = new SoapClient($nameSpace."?wsdl");
$pageNumber = 0;
//Setting up parameters
$params = array();
$params["apiKey"] = "MY_API_KEY";
//Value for parameters (optional)
//$params["networkId"] = array(1,2);
//$params["pageNumber"] = 0;
//$params["pageSize"] = 10;
//Making Request
$response = $client->getNetworks($params);
//XML to SimpleXMLElement Object
$xmlResponse = new SimpleXMLElement($response);
if ($xmlResponse->success == "true"){
while (isset($xmlResponse->dataList->data)) {
//Iterate the results
foreach ($xmlResponse->dataList->data as $data){
var_dump(xml2array($data));
}
//Requesting next page of data
$pageNumber++;
$params["pageNumber"] = $pageNumber;
//Making Request
$response = $client->getNetworks($params);
//XML to SimpleXMLElement Object
$xmlResponse = new SimpleXMLElement($response);
}
} else {
//Error somewhere
echo $xmlResponse->errorMessage;
}
/**
* Transforms the object SimpleXmlElement into an array, easier to handle
*/
function xml2array($xml) {
$arr = array();
foreach ($xml as $element) {
$tag = $element->getName();
$e = get_object_vars($element);
if (!empty($e)) {
$arr[$tag] = $element instanceof SimpleXMLElement ? xml2array($element) : $e;
} else {
$arr[$tag] = trim($element);
}
}
return $arr;
}
?>
this was the response for what i tried :
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://secure.affjet.com/ws/api"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:getTransactionsResponse>
<return xsi:type="xsd:string">
<response><success>false</success><errorMessage>
API Key not provided</errorMessage><dataList>
</dataList></response>
</return>
</ns1:getTransactionsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
you can see :
API Key not provided
And the response should be something like this:
<response>
<success>true</success>
<errorMessage></errorMessage>
<dataList>
<data>
<date>2012-11-05 15:02:41</date>//Transaction Date
<amount>81.67</amount>
<commission>15.86</commission>
<status>confirmed</status>//Status, could be: declined, pending, confirmed or paid
<clickDate></clickDate>
<ip></ip>
<custom_id>MyCustomId</custom_id>//Custom Id for the transactions (SID, SubId,clickRef....)
<unique_id>2548462</unique_id>//Unique Id given from the network to this transaction
<merchantId>1</merchantId>//Id for the Merchant on AffJet
<networkId>1</networkId>//Id for the Network on AffJet
</data>
</dataList>
</response>
all i need to supply is a parameter named "apiKey" and its value
EDIT :
after contacting their support, they said the request should look like this :
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://secure.affjet.com/ws/api"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns2="http://xml.apache.org/xml-soap"
xmlns:SOAP- ENC="http://schemas.xmlsoap.org/soap/encoding/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:getTransactions>
<params xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">apiKey</key>
<value xsi:type="xsd:string">YOURAPIKEY</value>
</item>
</params>
</ns1:getTransactions>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Any ideas ?
I dug a little deeper into this fascinating topic and have to say that working with what amounts to an associative array is just a pain in the .NET SOAP implementation. An associative array is represented as an IDictionary (e.g. Hashtable) – but refuses to be serialized (try it!).
The below code is the closest I could get – and for some reason (bug?) it does not work on the .NET framework, but does work on Mono.
using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Text;
public class Test
{
/*
* as per http://stackoverflow.com/a/1072815/2348103
*/
public class Item
{
[XmlElement(Form = XmlSchemaForm.Unqualified)]
public string key;
[XmlElement(Form = XmlSchemaForm.Unqualified)]
public string value;
}
[SoapType(TypeName = "Map", Namespace = "http://xml.apache.org/xml-soap")]
public class Map : List<Item>
{
}
public static void Main()
{
Map map = new Map();
map.Add( new Item { key="foo", value="bar" } );
map.Add( new Item { key="quux", value="barf" } );
XmlTypeMapping mapping =
(new SoapReflectionImporter()).ImportTypeMapping( map.GetType() );
XmlSerializer serializer = new XmlSerializer( mapping );
XmlTextWriter writer = new XmlTextWriter( System.Console.Out );
writer.Formatting = Formatting.Indented;
writer.WriteStartElement( "root" );
serializer.Serialize( writer, map );
writer.WriteEndElement();
writer.Close();
}
}
/*
//
// does not work - but see http://msdn.microsoft.com/en-us/magazine/cc164135.aspx
//
public class Map : IXmlSerializable
{
const string NS = "http://xml.apache.org/xml-soap";
public IDictionary dictionary;
public Map()
{
dictionary = new Hashtable();
}
public Map(IDictionary dictionary)
{
this.dictionary = dictionary;
}
public void WriteXml(XmlWriter w)
{
w.WriteStartElement("Map", NS);
foreach (object key in dictionary.Keys)
{
object value = dictionary[key];
w.WriteStartElement("item", NS);
w.WriteElementString("key", NS, key.ToString());
w.WriteElementString("value", NS, value.ToString());
w.WriteEndElement();
}
w.WriteEndElement();
}
public void ReadXml(XmlReader r)
{
r.Read(); // move past container
r.ReadStartElement("dictionary");
while (r.NodeType != XmlNodeType.EndElement)
{
r.ReadStartElement("item", NS);
string key = r.ReadElementString("key", NS);
string value = r.ReadElementString("value", NS);
r.ReadEndElement();
r.MoveToContent();
dictionary.Add(key, value);
}
}
public System.Xml.Schema.XmlSchema GetSchema() { return null; }
}
*/
Sample output from Mono:
<q1:Map xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="id1" d2p1:arrayType="Item[2]" xmlns:d2p1="http://schemas.xmlsoap.org/soap/encoding/" xmlns:q1="http://xml.apache.org/xml-soap">
<Item href="#id2" />
<Item href="#id3" />
</q1:Map>
[...]
Sample output from .NET:
<q1:Array xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="id1" q1:arrayType="Item[2]" xmlns:q1="http://schemas.xmlsoap.org/soap/encoding/">
<Item href="#id2" />
<Item href="#id3" />
</q1:Array>
[...]
Either you can use KeyValue pair class or Dictionary class as follows:
Dictionary<string, string> d = new Dictionary<string, string>();
d.Add("apiKey", "******");
var response = new object[] { d };
KeyValuePair<string, string> d = new KeyValuePair<string, string>("apiKey", "******");
var response = new object[] { d };
It appears the problem is the difference between array in PHP and C#. In PHP, its a key value pair. How doest his look in the generated class that was created from the wdsl? Here is a line to a SO question that is related to your issue.
C# equivalent to php associative array One answer says to try Dictionary<String, String>. Maybe be worth a try to use KeyValuePair<String, String> passed as an object array.
KeyValuePair<String, String> parm = new KeyValuePair<String, String>("apiKey","******");
var response = service.getTransactions(new object[] { parm });

Categories