I am trying to parse an XML response from a REST call. I can read the XML just fine with my stream reader but when I try to select the first node, it brings back nothing. Here is my XML:
<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<slot_meta_data xmlns:ns2="http://www.w3.org/2005/Atom">
<product id="MDP">
<name>MDP</name>
</product>
<product id="CTxP">
<name>CTxP</name>
</product>
<product id="STR">
<name>STR</name>
</product>
<product id="ApP">
<name>ApP</name>
<slot>
<agent_id>-1111</agent_id>
<agent_name>ApP</agent_name>
<index_id>-1</index_id>
<alias>App Alias</slot_alias>
</slot>
</product>
<product id="TxP">
<name>TxP</name>
<slot>
<agent_id>2222</agent_id>
<agent_name>App2</agent_name>
<index_id>-1</index_id>
<alias>App2 Alias</slot_alias>
</slot>
</product>
</slot_meta_data>
Here is my code
string newURL = "RESTURL";
HttpWebRequest request = WebRequest.Create(newURL) as HttpWebRequest;
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
StreamReader reader = new StreamReader(response.GetResponseStream());
XmlDocument xdoc = new XmlDocument();
xdoc.Load(response.GetResponseStream());
XmlNodeList list = xdoc.SelectNodes("/slot_meta_data[#*]");
foreach (XmlNode node in list)
{
XmlNode product = node.SelectSingleNode("product");
string name = product["name"].InnerText;
string id = product["id"].InnerText;
Console.WriteLine(name);
Console.WriteLine(id);
Console.ReadLine();
}
When I debug list, it has a count of 0.
Using xml linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication22
{
class Program
{
const string FILENAME = #"c:\TEMP\TEST.XML";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("product").Select(x => new {
name = (string)x.Element("name"),
slot = x.Elements("slot").Select(y => new {
agent_id = (int)y.Element("agent_id"),
agent_name = (string)y.Element("agent_name"),
index_id = (int)y.Element("index_id"),
slot_alias = (string)y.Element("slot_alias")
}).FirstOrDefault()
}).ToList();
}
}
}
Related
I have an XML document with ISO-8859-1 encoding.
I can load the XML with XDOCUMENT, but i can't storage a specific node.
Can somebody help me with this?
The xml:
<?xml version="1.0" ?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<TERMEKADATOutput xmlns="http://xmlns.oracle.com/orawsv/PUPHAX/PUPHAXWS">
<RETURN>
<OBJTERMEKADAT>
<TTT>210037069</TTT>
<TK>OGYI-T-04617/05</TK>
</OBJTERMEKADAT>
</RETURN>
</TERMEKADATOutput>
</soap:Body>
</soap:Envelope>
And the code:
XDocument xmlDoc = null;
using (StreamReader oReader = new StreamReader(#"C:\Users\maruzsama\source\repos\EPPuphax\EPPuphax\asd.xml", Encoding.GetEncoding("ISO-8859-1")))
{
xmlDoc = XDocument.Load(oReader); //this working fine
var result = from q in xmlDoc.Descendants("OBJTERMEKADAT")
select new
{
asd = q.Element("TTT").Value
};
Console.ReadKey();
}
I need the data from the TTT node (210037069)
Try following changes :
XDocument xmlDoc = null;
using (StreamReader oReader = new StreamReader(#"C:\Users\maruzsama\source\repos\EPPuphax\EPPuphax\asd.xml", Encoding.GetEncoding("ISO-8859-1")))
{
xmlDoc = XDocument.Load(oReader); //this working fine
XElement TERMEKADATOutput = xmlDoc.Descendants().Where(x => x.Name.LocalName == "TERMEKADATOutput").First();
XNamespace ns = TERMEKADATOutput.GetDefaultNamespace();
var result = from q in TERMEKADATOutput.Descendants(ns + "OBJTERMEKADAT")
select new
{
asd = q.Element(ns + "TTT").Value
};
Console.ReadKey();
}
I'm trying to get the element values of the following XML document using LINQ but I'm getting "Object reference not set to an instance of an object.";
XML:
<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>
<ns1:GetContactResponse soapenv:encodingStyle=http://schemas.xmlsoap.org/soap/encoding/ xmlns:ns1=http://DefaultNamespace>
<GetContactReturn xsi:type="ns2:Response" xmlns:ns2=http://getContact.contact.V300>
<Contact xsi:type="ns3:Contact" xmlns:ns3=http://_contact.contact.V300>
<Address xsi:type="xsd:string">123 test</Address>
<Address2 xsi:type="xsd:string"/>
<City xsi:type="xsd:string">Los Angeles</City>
<CountryCode xsi:type="xsd:string">US</CountryCode>
<StateCode xsi:type="xsd:string">CA</StateCode>
<ZipCode xsi:type="xsd:string">90001</ZipCode>
</Contact>
<Errors soapenc:arrayType="ns4:Error[0]" xsi:type="soapenc:Array" xmlns:ns4=http://response.base.V300 xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding//>
<IsSuccessful xsi:type="xsd:boolean">true</IsSuccessful>
<RecordCount xsi:type="xsd:double">1.0</RecordCount>
</GetContactReturn>
</ns1:GetContactResponse>
</soapenv:Body>
</soapenv:Envelope>
I'm using the following to loop through:
using (StreamReader rd = new StreamReader(services.GetResponseStream()))
{
var ServiceResult = rd.ReadToEnd();
XDocument xdoc = new XDocument();
xdoc = XDocument.Parse(ServiceResult);
var xDocResult = (from x in xdoc.Descendants("Contacts")
select new Location
{
Address = x.Element("Address").Value,
Address2 = x.Element("Address2").Value,
city = x.Element("City").Value,
statecode = x.Element("StateCode").Value,
zipcode = x.Element("ZipCode").Value
});
}
I'm assuming it's because there is no root. How do I get the values of the individual elements?
Your xml has lots of errors. I'm using StringReader. You should replace with StreamReader. Here is correction
<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>
<ns1:GetContactResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://DefaultNamespace">
<GetContactReturn xsi:type="ns2:Response" xmlns:ns2="http://getContact.contact.V300">
<Contact xsi:type="ns3:Contact" xmlns:ns3="http://_contact.contact.V300">
<Address xsi:type="xsd:string">123 test</Address>
<Address2 xsi:type="xsd:string"/>
<City xsi:type="xsd:string">Los Angeles</City>
<CountryCode xsi:type="xsd:string">US</CountryCode>
<StateCode xsi:type="xsd:string">CA</StateCode>
<ZipCode xsi:type="xsd:string">90001</ZipCode>
</Contact>
<Errors soapenv:arrayType="ns4:Error[0]" xsi:type="soapenc:Array" xmlns:ns4="http://response.base.V300 xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding"/>
<IsSuccessful xsi:type="xsd:boolean">true</IsSuccessful>
<RecordCount xsi:type="xsd:double">1.0</RecordCount>
</GetContactReturn>
</ns1:GetContactResponse>
</soapenv:Body>
</soapenv:Envelope>
Here is the c# code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
string xml = File.ReadAllText(FILENAME);
StringReader reader = new StringReader(xml);
XDocument doc = XDocument.Load(reader);
XElement contact = doc.Descendants().Where(x => x.Name.LocalName == "Contact").FirstOrDefault();
XNamespace ns = contact.GetDefaultNamespace();
string address = (string)contact.Element(ns + "Address");
string address2 = (string)contact.Element(ns + "Address2");
string city = (string)contact.Element(ns + "City");
string country = (string)contact.Element(ns + "CountryCode");
string state = (string)contact.Element(ns + "StateCode");
string zip = (string)contact.Element(ns + "ZipCode");
}
}
}
I've created a program to retrieve data from a database and convert it to XML. It returns a list of 24 objects that I created:
<?xml version = "1.0" encoding = "utf-16"
<ArrayOfData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Data>
<interval>01</interval>
<num>12345</num>
<id>ABC123 </id>
<type>LETTER</type>
<party>1</party>
<amount>-10</amount>
</Data>
<Data>
<interval>02</interval>
<num>12345</num>
<id>ABC123 </id>
<type>LETTER</type>
<party>1</party>
<amount>-11</amount>
</Data>
<Data>
<interval>03</interval>
<num>12345</num>
<id>ABC123 </id>
<type>LETTER</type>
<party>1</party>
<amount>-12</amount>
</Data>
<Data>
<interval>04</interval>
<num>12345</num>
<id>ABC123 </id>
<type>LETTER</type>
<party>1</party>
<amount>-13</amount>
</Data>
Basically the data goes on with incrementing intervals by 1 up to 24.
What I am looking for is to look for what is duplicate and just concatenate them into one child, and then to look for what differs and create a different element for it but keep them everything in one child. How can I write the XML to do this? Example:
<Data>
<num>12345</num>
<id>ABC123 </id>
<type>LETTER</type>
<party>1</party>
<details>
<interval>01</interval>
<amount>-10</amount>
</details>
<details>
<interval>02</interval>
<amount>-11</amount>
</details>
<details>
<interval>03</interval>
<amount>-12</amount>
</details>
<details>
<interval>04</interval>
<amount>-13</amount>
</details>
</Data>
I want to create a function that returns this XML. The parameters in my current function now are just there to get the data using those parameters. Here's how I generate the XML now:
public IActionResult CreateXML(int num, int id)
{
string xml = "";
var temp = (from x in Context.InternalData.AsNoTracking() where (x.num == num && x.id == id) select x).ToList();
using (var stringwrite = new Utf8StringWriter())
{
var serializer = new XmlSerializer(temp.GetType());
serializer.Serialize(stringwrite, temp);
xml = stringwrite.ToString();
StringReader reader = new StringReader(stringwriter.ToString());
reader.ReadLine(); //skips the UTF-16 header
XDocument doc = XDocument.Load(reader);
XElement arrayOfData = doc.Root;
List<XElement> newData = new List<XElement>();
var parties = doc.Descendants("Data")
.GroupBy(x => (string)x.Element("id"))
.ToList();
foreach (var party in parties)
{
XElement template = party.First();
List<XElement> details = party.Select(x => new XElement("details", new object[] {
new XElement("interval", (string)x.Element("interval")),
new XElement("amount", (string)x.Element("amount"))
})).ToList();
template.Element("interval").Remove();
template.Element("amount").Remove();
template.Add(details);
newData.Add(template);
}
arrayOfData.ReplaceAll(newData);
return Ok(arrayOfData);
}
}
I am new to this and have never worked with generating XML before. Any help would be greatly appreciated!
Try xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
string xml = File.ReadAllText(FILENAME);
StringWriter stringwriter = new StringWriter();
stringwriter.Write(xml);
StringReader reader = new StringReader(stringwriter.ToString());
reader.ReadLine(); //skips the UTF-16 header
XDocument doc = XDocument.Load(reader);
XElement arrayOfData = doc.Root;
List<XElement> newData = new List<XElement>();
var parties = doc.Descendants("Data")
.GroupBy(x => (string)x.Element("id"))
.ToList();
foreach (var party in parties)
{
XElement template = party.First();
List<XElement> details = party.Select(x => new XElement("details", new object[] {
new XElement("interval", (string)x.Element("interval")),
new XElement("amount", (string)x.Element("amount"))
})).ToList();
template.Element("interval").Remove();
template.Element("amount").Remove();
template.Add(details);
newData.Add(template);
}
arrayOfData.ReplaceAll(newData);
}
}
}
I have following SOAP response. Using my code I could only extract one element.
and then it throws null reference exception error.
How do I extract Item->key and Item->value in to my dictionary in the function in C#?
Following is a part of the function which i use to extract data from.
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP- ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:Magento" xmlns:ns2="http://xml.apache.org/xml-soap" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:callResponse>
<callReturn SOAP-ENC:arrayType="ns2:Map[2]" xsi:type="SOAP-ENC:Array">
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">state</key>
<value xsi:type="xsd:string">processing</value>
</item>
<item>
<key xsi:type="xsd:string">status</key>
<value xsi:type="xsd:string">processing</value>
</item>
<item>
<key xsi:type="xsd:string">protect_code</key>
<value xsi:type="xsd:string">ba8dd7</value>
</item>
<item>
<key xsi:type="xsd:string">shipping_firstname</key>
<value xsi:type="xsd:string">Roshan</value>
</item>
<item>
<key xsi:type="xsd:string">billing_name</key>
<value xsi:type="xsd:string">Roshan India</value>
</item>
<item>
<key xsi:type="xsd:string">shipping_name</key>
<value xsi:type="xsd:string">Roshan India</value>
</item>
<item>
<key xsi:type="xsd:string">order_id</key>
<value xsi:type="xsd:string">2</value>
</item>
</item>
</callReturn>
</ns1:callResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The codes,
try
{
XmlDocument xdoc = new XmlDocument();//xml doc used for xml parsing
xdoc.LoadXml(content); // --------> THIS IS WHERE I PASS SOAP RESPONSE
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xdoc.NameTable);
nsmgr.AddNamespace("SOAP-ENV:encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/");
XmlNodeList xNodelst = xdoc.DocumentElement.SelectNodes("//item");
int nodes = xNodelst.Count;
dynamic item;
Dictionary<string, string> items = new Dictionary<string, string>();
foreach (XmlNode xn in xNodelst)
{
XmlNode itemnode = xn["item"];
if (itemnode != null) {
string key = itemnode["key"].InnerText;
string value = itemnode["value"].InnerText;
items.Add(key, value);
}
}
var ss = items;
return "";
}
catch (Exception e) {
return e.Message;
}
The xml has two level of the tag item. So you have to make sure you treat each level of the item tag separately. I used xml linq to get data and put results into a dictionary.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement root = doc.Root;
XNamespace ns = root.GetDefaultNamespace();
XElement firstItem = doc.Descendants(ns + "item").FirstOrDefault();
Dictionary<string, string> dictItems = firstItem.Descendants(ns + "item")
.GroupBy(x => (string)x.Element("key"), y => (string)y.Element("value"))
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
}
}
I finally managed to make this work. I added all the namespaces in namespace manager variable. And used xPath to navigate through the XML as below,
XmlNodeList xNodelst = xdoc.DocumentElement.SelectNodes("//item[#xsi:type]",nsmgr);
This helped me to resolve my problem. I'm posting this here in case if anyone having similar issue.
Cheers!
string soapString = #"<soapenv:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:urn=""urn:Magento""><soapenv:Header/><soapenv:Body><urn:call soapenv:encodingStyle=""http://schemas.xmlsoap.org/soap/encoding/""><sessionId xsi:type=""xsd:string"">"+ssid +#"</sessionId><resourcePath xsi:type=""xsd:string"">sales_order.list</resourcePath><args xsi:type=""xsd:anyType""></args></urn:call></soapenv:Body></soapenv:Envelope>";
HttpResponseMessage response = await PostXmlRequest("http://localhost/M1/api.php", soapString);
string content = await response.Content.ReadAsStringAsync();
XmlDocument xdoc = new XmlDocument();//xml doc used for xml parsing
xdoc.LoadXml(content);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xdoc.NameTable);
nsmgr.AddNamespace("ns1", "urn:Magento");
nsmgr.AddNamespace("ns2", "http://xml.apache.org/xml-soap");
nsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
nsmgr.AddNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
nsmgr.AddNamespace("SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/");
nsmgr.AddNamespace("encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/");
XmlNodeList xNodelst = xdoc.DocumentElement.SelectNodes("//item[#xsi:type]",nsmgr);
int nodes = xNodelst.Count;
Dictionary<int, IDictionary> items = new Dictionary<int, IDictionary>();
int n = 0;
foreach (XmlNode xn in xNodelst)
{
XmlNode itemnode = xn;
Dictionary<string, string> itemData = new Dictionary<string, string>();
foreach (XmlNode xn1 in itemnode)
{
string key = xn1["key"].InnerText;
string value = xn1["value"].InnerText;
itemData.Add(key, value);
}
items.Add(n, itemData);
n++;
}
return items;
Aim:
- Deserialize data from an xml document and storing it as an array.
- Avoiding manually assigning the data to different strings.
- The xml document will be manually generated
public void DeserializeObject(string filename)
{
try
{
XmlSerializer deserializer = new XmlSerializer(typeof(string[]));
FileStream fs = new FileStream(filename, FileMode.Open);
string[] XmlData = (string[])deserializer.Deserialize(fs);
foreach (string p in XmlData)
{
Console.WriteLine(p);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
The XML document is as follows
<?xml version="1.0" encoding="utf-8"?>
<Mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Products>
<Product>
<software>Seiko</software>
</Product>
<Product>
<hardware>Martina</hardware>
</Product>
</Products>
</Mapping>
Thank you, found this solution
<?xml version="1.0" encoding="utf-8" ?>
<Locations>
<Location Name="Location1" IP="127.0.0.1"></Location>
<Location Name="Location2" IP="127.0.0.1"></Location>
<Location Name="Location3" IP="127.0.0.1"></Location>
<Location Name="Location4" IP="127.0.0.1"></Location>
<Location Name="Location5" IP="127.0.0.1"></Location>
</Locations>
using System.Xml.Linq;
class Program
{
static void Main(string[] args)
{
string[] strarr = GetStringArray("Locations.xml");
foreach (string str in strarr)
{
Console.WriteLine(str);
}
}
public static string[] GetStringArray(string url)
{
XDocument doc = XDocument.Load(url);
var locations = from l in doc.Descendants("Location")
select (string)l.Attribute("Name");
return locations.ToArray();
}
}
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication38
{
class Program
{
static void Main(string[] args)
{
string input =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<Mapping xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
"<Products>" +
"<Product>" +
"<software>Seiko</software>" +
"</Product>" +
"<Product>" +
"<hardware>Martina</hardware>" +
"</Product>" +
"</Products>" +
"</Mapping>";
XDocument doc = XDocument.Parse(input);
var results = doc.Descendants("Product").Select(x =>
x.Elements().Select(y => new { type = y.Name, value = (string)y }).ToList()
).SelectMany(z => z).ToList();
var groups = results.GroupBy(x => x.type).ToList();
}
}
}
You need to generate a class from your sample XML.
You can use the xsd.exe to generate a .xsd and from that create a .cs file.
The you need to add this type to your XmlSerializer
See this answer: Generate C# class from XML
XmlSerializer deserializer = new XmlSerializer(typeof(Mapping)); <- Created class type here.
If all you want to do is get the data form the XML-document as an array of strings, you can use XmlDocument to load in the data
XmlDocument doc = new XmlDocument();
doc.Load("file.xml");
Then you can find the nodes you need using an xPath expression:
XmlNodeList nodelist = doc.SelectNodes(...);