Writing XML based on dynamically generated data - c#

I will be writing an XML file via C# where the data for the elements is dynamic. So typically, I'll have this structure (simplified for the question):
<?xml version="1.0" encoding="utf-8"?>
<Output xmlns="http://xxxx/xxx.xsd">
<AccountHolder>
<Name></Name>
<Address1></Address1>
<City></City>
<State></State>
<ZipCode></ZipCode>
</AccountHolder>
<Visits>
<VisitDate></VisitDate>
<Copay></Copay>
<CoInsurance></CoInsurance>
</Visits>
<PatientDetails>
<AcctNo></AcctNo>
<PatientName></PatientName>
<Medicare></Medicare>
<VisitDetails>
<VDate></VDate>
<Amount></Amount>
<NonCoveredAmount></NonCoveredAmount>
</VisitDetails>
</PatientDetails>
</Output>
Now, while there will always be one "Account Holder" there will be anywhere from 0 to multiple visits. Subsequently, there will be list of 0 or more Patients, and then nested within the patients, there will be 0 or more visit details.
I do not control the structure. I need to take the collected data I receive and create the XML. I'll be receiving data on a single account holder which may have any number of the subsequent elements.
I have classes for AccountHolder, Visit, PatientDetails, and VisitDetails. However, I am unsure as to the best method for building out the XML dynamically as I read the source data? At first, I was thinking on gathering the data in various collections.

Try following :
using System;
using System.Linq;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApp2
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
Output output = new Output()
{
Accountholder = new AccountHolder()
{
Name = "",
Address1 = "",
City = "",
State = "",
ZipCode = ""
},
Visits = new Visits(),
PatientDetails = new PatientDetails()
{
AccNo = "",
PatientName = "",
Medicare = "",
VisitDetails = new List<VisitDetails>()
{
new VisitDetails()
}
}
};
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, "http://xxxx/xxx.xsd");
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter writer = XmlWriter.Create(FILENAME, settings);
XmlSerializer serializer = new XmlSerializer(typeof(Output));
serializer.Serialize(writer, output);
}
}
[XmlRoot(Namespace = "http://xxxx/xxx.xsd")]
public class Output
{
public AccountHolder Accountholder { get; set; }
public Visits Visits { get; set; }
public PatientDetails PatientDetails { get; set; }
}
public class AccountHolder
{
public string Name { get; set; }
public string Address1 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
}
public class Visits
{
public DateTime VisitDate { get; set; }
public decimal Copay { get; set; }
public decimal CoInsurance { get; set; }
}
public class PatientDetails
{
public string AccNo { get; set; }
public string PatientName { get; set; }
public string Medicare { get; set; }
[XmlElement]
public List<VisitDetails> VisitDetails { get; set; }
}
public class VisitDetails
{
public DateTime VDate { get; set; }
public decimal Amount { get; set; }
public decimal NonCoveredAmount { get; set; }
}
}

Related

How do I Deserialize this XML document in C#

This is my XML:
<?xml version="1.0" encoding="utf-8"?>
<Fruit>
<Fruit_group name='Tropical'>
<fruit_types name ='Tropical Used'>
<fruit>bananas</fruit>
<fruit>mangoes</fruit>
</fruit_types>
</Fruit_group>
<Fruit_group name='Citrus'>
<fruit_types name ='Citruses Used'>
<fruit>orange</fruit>
<fruit>lime</fruit>
<fruit>grapefruit</fruit>
<excluded_fruits>
<fruit>mandarin</fruit>
</excluded_fruits>
</fruit_types>
</Fruit_group>
</Fruit>
And this is a sample of my XML text..
Is there a way to deserialize it and keep the name of the elements too..
I mean I would like to have like:
Tropical -> Tropical Used -> fruit -> bananas, mangoes
Citrus->Citruses Used ->
fruit = orange, lime, grapefruit
excluded = mandarin....
Something like this...
Could someone help me to understand how does this work?
You can use XML to c# website to create a class according to XML.
In this case, I've got this example
using System;
using System.Xml.Serialization;
using System.Collections.Generic;
namespace Xml2CSharp
{
[XmlRoot(ElementName="fruit_types")]
public class Fruit_types {
[XmlElement(ElementName="fruit")]
public List<string> Fruit { get; set; }
[XmlAttribute(AttributeName="name")]
public string Name { get; set; }
[XmlElement(ElementName="excluded_fruits")]
public Excluded_fruits Excluded_fruits { get; set; }
}
[XmlRoot(ElementName="Fruit_group")]
public class Fruit_group {
[XmlElement(ElementName="fruit_types")]
public Fruit_types Fruit_types { get; set; }
[XmlAttribute(AttributeName="name")]
public string Name { get; set; }
}
[XmlRoot(ElementName="excluded_fruits")]
public class Excluded_fruits {
[XmlElement(ElementName="fruit")]
public string Fruit { get; set; }
}
[XmlRoot(ElementName="Fruit")]
public class Fruit {
[XmlElement(ElementName="Fruit_group")]
public List<Fruit_group> Fruit_group { get; set; }
}
}
Also, You could read this article about XML deserialization.
I suggest using xmltocsharp to convert the XML to Model and do required alterations. For example change the string to List<string> under Excluded_fruits
[XmlRoot(ElementName = "fruit_types")]
public class Fruit_types
{
[XmlElement(ElementName = "fruit")]
public List<string> Fruit { get; set; }
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
[XmlElement(ElementName = "excluded_fruits")]
public Excluded_fruits Excluded_fruits { get; set; }
}
[XmlRoot(ElementName = "Fruit_group")]
public class Fruit_group
{
[XmlElement(ElementName = "fruit_types")]
public Fruit_types Fruit_types { get; set; }
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
}
[XmlRoot(ElementName = "excluded_fruits")]
public class Excluded_fruits
{
[XmlElement(ElementName = "fruit")]
public List<string> Fruit { get; set; }
}
[XmlRoot(ElementName = "Fruit")]
public class Fruit
{
[XmlElement(ElementName = "Fruit_group")]
public List<Fruit_group> Fruit_group { get; set; }
}
Once the model is ready, you can use XmlSerializer to Deserialize the xml file
using (StreamReader r = new StreamReader(xmlfilepath))
{
string xmlString = r.ReadToEnd();
XmlSerializer ser = new XmlSerializer(typeof(Fruit));
using (TextReader reader = new StringReader(xmlString))
{
var fruits = (Fruit)ser.Deserialize(reader);
foreach(var fruitgroup in fruits.Fruit_group)
{
Console.Write($"{fruitgroup.Name} -> ");
Console.Write($"{fruitgroup.Fruit_types.Name} -> ");
Console.Write($"fruit = {string.Join(",", fruitgroup.Fruit_types.Fruit.ToArray())}; ");
if (fruitgroup.Fruit_types.Excluded_fruits?.Fruit?.Count() > 0)
Console.Write($"excluded = {string.Join(",", fruitgroup.Fruit_types.Excluded_fruits.Fruit.ToArray())}{Environment.NewLine}");
else
Console.WriteLine();
}
}
}
I like getting flatter results doing a custom parsing using xml linq :
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);
List<Fruit_Group> groups = doc.Descendants("Fruit_group").Select(x => new Fruit_Group()
{
groupName = (string)x.Attribute("name"),
typeName = (string)x.Element("fruit_types").Attribute("name"),
fruits = x.Elements("fruit_types").Elements("fruit").Select(y => (string)y).ToArray(),
excluded = x.Descendants("excluded_fruits").Elements("fruit").Select(y => (string)y).ToArray()
}).ToList();
}
}
public class Fruit_Group
{
public string groupName { get; set;}
public string typeName { get; set; }
public string[] fruits { get; set; }
public string[] excluded { get; set; }
}
}

XML not being serialised correctly into object

I am trying to serialise a piece of XML that is being returned from a third party API. However when doing so i am only retrieving part of the object upon serialisation. And only some of the values seem to exist. I thought this could be a input type problem, however all the types seem to be correct. I think it may have something to do with how my model is constructed.After debugging the code i have narrowed it down to be a problem with the conversion of the xml into the object.
C# CODE:
//[Route("api/AvailabiliyCheck/GetAvailability/{CSSDistrictCode}/{GoldAddressKey}")]
public EADAvailabilityDetails GetAvailabilityEAD([FromUri] string CSSDistrictCode, [FromUri] string GoldAddressKey)
{
//Load the request xml template
XmlDocument doc = new XmlDocument();
string path = HttpContext.Current.Server.MapPath("~/XML/Availability/GetAvailabilityEAD.xml");
doc.Load(path);
//Assign incoming paramaters to xml template
XmlNamespaceManager manager = new XmlNamespaceManager(doc.NameTable);
manager.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
manager.AddNamespace("stupid_xmlns", "http://webservices.talktalkplc.com/NetworkProductAvailabilityCheckerService");
XmlNode CSSDistrictCodeNode = doc.SelectSingleNode("soap:Envelope/soap:Body/stupid_xmlns:GetAvailability/stupid_xmlns:request/stupid_xmlns:RequestDetails/stupid_xmlns:CSSDistrictCode", manager);
CSSDistrictCodeNode.InnerXml = CSSDistrictCode;
XmlNode GoldAddressKeyNode = doc.SelectSingleNode("soap:Envelope/soap:Body/stupid_xmlns:GetAvailability/stupid_xmlns:request/stupid_xmlns:RequestDetails/stupid_xmlns:GoldAddressKey", manager);
GoldAddressKeyNode.InnerXml = GoldAddressKey;
//Send Request To API
string _url = "https://b2b.api.talktalkgroup.com/api/v2/partners/AvailabilityCheckers/NPAC/v45";
string _action = "http://webservices.talktalkplc.com/NetworkProductAvailabilityCheckerService/NetworkProductAvailabilityCheckerService/GetAvailability";
string xml = doc.InnerXml;
var soapResult = WebService.ApiRequest(_url, _action, xml);
XmlDocument xmlToFormat = new XmlDocument();
xmlToFormat.LoadXml(soapResult);
string Outerxml = xmlToFormat.FirstChild.FirstChild.FirstChild.FirstChild.ChildNodes[2].InnerXml;
//Remove all namespaces
var xmlToBeStripped = XElement.Parse(Outerxml);
string finalXml = XmlFormatter.stripNS(xmlToBeStripped).ToString();
EADAvailabilityDetails result;
// Deserialises xlm into an object
XmlSerializer serializer = new XmlSerializer(typeof(EADAvailabilityDetails));
using (TextReader reader = new StringReader(finalXml))
{
result = (EADAvailabilityDetails)serializer.Deserialize(reader);
}
return result;
}
XML being returned:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetAvailabilityResponse xmlns="http://webservices.talktalkplc.com/NetworkProductAvailabilityCheckerService">
<GetAvailabilityResult xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Status xmlns="http://schemas.datacontract.org/2004/07/InHouse.SharedLibraries.ServiceBase.BaseTypes">
<Errors/>
<HasErrors>false</HasErrors>
</Status>
<CSSDistrictCode>lv</CSSDistrictCode>
<EADAvailability>
<AvailabilityDetails i:type="EADAvailabilityDetails">
<Status xmlns="http://schemas.datacontract.org/2004/07/InHouse.SharedLibraries.ServiceBase.BaseTypes">
<Errors/>
<HasErrors>false</HasErrors>
</Status>
<EADAvailability>
<EADAvailabilityResult>
<CollectorNodeExchangeCode>NDMAI</CollectorNodeExchangeCode>
<CollectorNodeExchangeName>Maidstone</CollectorNodeExchangeName>
<Distance>0</Distance>
<EADBandwidth xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a:string>100M</a:string>
</EADBandwidth>
<EADSubType>EAD-LA</EADSubType>
<FibreExchangeCode>NDACO</FibreExchangeCode>
<FibreExchangename>Archers Court</FibreExchangename>
<IndicativeECC>Within tariff</IndicativeECC>
<IndicativeOrderCategory>Category 2.1</IndicativeOrderCategory>
<LocalExchangeCode>NDACO</LocalExchangeCode>
<LocalExchangeName>Archers Court</LocalExchangeName>
<ORLeadTime>40</ORLeadTime>
<OrderCategoryExplanation>There is a T node within 1km (or 650m in London) with spare capacity and ducting is in place, however some additional cabling is required between the premises and the T node.</OrderCategoryExplanation>
<TTLeadTime>56</TTLeadTime>
<Zone>0</Zone>
</EADAvailabilityResult>
<EADAvailabilityResult>
<CollectorNodeExchangeCode>NDMAI</CollectorNodeExchangeCode>
<CollectorNodeExchangeName>Maidstone</CollectorNodeExchangeName>
<Distance>0</Distance>
<EADBandwidth xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a:string>LOW 1GB</a:string>
</EADBandwidth>
<EADSubType>EAD-LA</EADSubType>
<FibreExchangeCode>NDACO</FibreExchangeCode>
<FibreExchangename>Archers Court</FibreExchangename>
<IndicativeECC>Within tariff</IndicativeECC>
<IndicativeOrderCategory>Category 2.1</IndicativeOrderCategory>
<LocalExchangeCode>NDACO</LocalExchangeCode>
<LocalExchangeName>Archers Court</LocalExchangeName>
<ORLeadTime>40</ORLeadTime>
<OrderCategoryExplanation>There is a T node within 1km (or 650m in London) with spare capacity and ducting is in place, however some additional cabling is required between the premises and the T node.</OrderCategoryExplanation>
<TTLeadTime>56</TTLeadTime>
<Zone>0</Zone>
</EADAvailabilityResult>
<EADAvailabilityResult>
<CollectorNodeExchangeCode>NDCAN</CollectorNodeExchangeCode>
<CollectorNodeExchangeName>Canterbury</CollectorNodeExchangeName>
<Distance>20656</Distance>
<EADBandwidth xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a:string>HIGH 1GB</a:string>
</EADBandwidth>
<EADSubType>EAD-NonLA</EADSubType>
<FibreExchangeCode>NDCAN</FibreExchangeCode>
<FibreExchangename>Canterbury</FibreExchangename>
<IndicativeECC>Within tariff</IndicativeECC>
<IndicativeOrderCategory>Category 2.1</IndicativeOrderCategory>
<LocalExchangeCode>NDACO</LocalExchangeCode>
<LocalExchangeName>Archers Court</LocalExchangeName>
<ORLeadTime>40</ORLeadTime>
<OrderCategoryExplanation>There is a T node within 1km (or 650m in London) with spare capacity and ducting is in place, however some additional cabling is required between the premises and the T node.</OrderCategoryExplanation>
<TTLeadTime>56</TTLeadTime>
<Zone>B</Zone>
</EADAvailabilityResult>
</EADAvailability>
<LeadTime>10</LeadTime>
</AvailabilityDetails>
</EADAvailability>
<GoldAddressKey>A00009292752</GoldAddressKey>
<Postcode/>
</GetAvailabilityResult>
</GetAvailabilityResponse>
</s:Body>
</s:Envelope>
Model:
[Serializable, XmlRoot("AvailabilityDetails")]
public class EADAvailabilityDetails
{
[XmlElement("EADAvailability")]
public EADAvailability EADAvailability { get; set; }
}
public class EADAvailability
{
[XmlElement("EADAvailabilityResult")]
public List<EADAvailabilityResult> EADAvailabilityResult { get; set; }
}
public class EADAvailabilityResult
{
[XmlElement("CollectorNodeExchangeCode")]
public string CollectorNodeExchangeCode { get; set; }
[XmlElement("CollectorNodeExchangeName")]
public string CollectorNodeExchangeName { get; set; }
[XmlElement("Distance")]
public int Distance { get; set; }
[XmlElement("EADBandwidth")]
public string EADBandwidth { get; set; }
[XmlElement("EADSubType")]
public string EADSubType { get; set; }
[XmlElement("FibreExchangeCode")]
public string FibreExchangeCode { get; set; }
[XmlElement("FibreExchangename")]
public string FibreExchangename { get; set; }
[XmlElement("IndicativeECC")]
public string IndicativeECC { get; set; }
[XmlElement("IndicativeOrderCategory")]
public string IndicativeOrderCategory { get; set; }
[XmlElement("LocalExchangeCode")]
public string LocalExchangeCode { get; set; }
[XmlElement("LocalExchangeName")]
public string LocalExchangeName { get; set; }
[XmlElement("ORLeadTime")]
public int ORLeadTime { get; set; }
[XmlElement("OrderCategoryExplanation")]
public string OrderCategoryExplanation { get; set; }
[XmlElement("TTLeadTime")]
public int TTLeadTime { get; set; }
[XmlElement("Zone")]
public int Zone { get; set; }
}
XML after serialisation:
{
"<EADAvailability>k__BackingField": {
"EADAvailabilityResult": [
{
"CollectorNodeExchangeCode": "NDMAI",
"CollectorNodeExchangeName": "Maidstone",
"Distance": 0,
"EADBandwidth": "100M",
"EADSubType": null,
"FibreExchangeCode": null,
"FibreExchangename": null,
"IndicativeECC": null,
"IndicativeOrderCategory": null,
"LocalExchangeCode": null,
"LocalExchangeName": null,
"ORLeadTime": 0,
"OrderCategoryExplanation": null,
"TTLeadTime": 0,
"Zone": 0
}
]
}
}
The code below works :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication139
{
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(Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class Envelope
{
[XmlElement (Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public Body Body { get; set; }
}
public class Body
{
[XmlElement(Namespace = "http://webservices.talktalkplc.com/NetworkProductAvailabilityCheckerService")]
public GetAvailabilityResponse GetAvailabilityResponse { get; set; }
}
public class GetAvailabilityResponse
{
[XmlElement(Namespace = "http://webservices.talktalkplc.com/NetworkProductAvailabilityCheckerService")]
public GetAvailabilityResult GetAvailabilityResult { get; set; }
}
public class GetAvailabilityResult
{
[XmlArray("EADAvailability", Namespace = "http://webservices.talktalkplc.com/NetworkProductAvailabilityCheckerService")]
[XmlArrayItem("AvailabilityDetails", Namespace = "http://webservices.talktalkplc.com/NetworkProductAvailabilityCheckerService")]
public AvailabilityDetails[] AvailabilityDetails { get; set; }
}
[XmlInclude(typeof(EADAvailabilityDetails))]
[Serializable, XmlRoot("AvailabilityDetails", Namespace = "http://webservices.talktalkplc.com/NetworkProductAvailabilityCheckerService")]
public class AvailabilityDetails
{
}
[Serializable, XmlRoot("EADAvailabilityDetails", Namespace = "http://webservices.talktalkplc.com/NetworkProductAvailabilityCheckerService")]
public class EADAvailabilityDetails : AvailabilityDetails
{
[XmlArray("EADAvailability", Namespace = "http://webservices.talktalkplc.com/NetworkProductAvailabilityCheckerService")]
[XmlArrayItem("EADAvailabilityResult", Namespace = "http://webservices.talktalkplc.com/NetworkProductAvailabilityCheckerService")]
public EADAvailabilityResult[] EADAvailabilityResult { get; set; }
}
public class EADAvailabilityResult
{
public string CollectorNodeExchangeCode { get; set; }
public string CollectorNodeExchangeName { get; set; }
public int Distance { get; set; }
public EADBandwidth EADBandwidth { get; set; }
public string EADSubType { get; set; }
public string FibreExchangeCode { get; set; }
public string FibreExchangename { get; set; }
public string IndicativeECC { get; set; }
public string IndicativeOrderCategory { get; set; }
public string LocalExchangeCode { get; set; }
public string LocalExchangeName { get; set; }
public int ORLeadTime { get; set; }
public string OrderCategoryExplanation { get; set; }
public int TTLeadTime { get; set; }
public string Zone { get; set; }
}
public class EADBandwidth
{
[XmlElement(ElementName = "string", Type = typeof(string), Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
public string String { get; set; }
}
}

Parsing XML in C# with variable children structure

I need to parse an XML file with the following structure: EDITED
…
<?xml version="1.0" encoding="windows-1252" ?>
<TABLE>
<COMPO>
<alim_code> 1000 </alim_code>
<const_code> 56700 </const_code>
<teneur> 0 </teneur>
<min missing=" " />
<max missing=" " />
<code_confiance> D </code_confiance>
<source_code missing="." />
</COMPO>
<COMPO>
<alim_code> 1000 </alim_code>
<const_code> 60000 </const_code>
<teneur> 37,5 </teneur>
<min> 37,3 </min>
<max> 37,6 </max>
<code_confiance> D </code_confiance>
<source_code> 38 </source_code>
</COMPO>
</TABLE>
…
As you can see several fields are described differently when the value is known or missing. I have tried to use ReadXml() in a DataSet but it does not seems to work on "variable structure".
Looks like the solution is probably to use Xdocument and LINQ but I am ignorant about LINQ and I did not succeed to write a working code.
I would appreciate if someone can show me possible code to parse and print (or even better add to a db) the content of this kind of XML file.
Linq will not be helpful here. What you can do is create the DTO for your xml using the sample xml that contains all the attributes and nodes which can be returned in the xml.
and then deserialize the xml in to that type.
Your classes would be something like following from the xml above which are being generated:
[XmlRoot(ElementName="min")]
public class Min {
[XmlAttribute(AttributeName="missing")]
public string Missing { get; set; }
}
[XmlRoot(ElementName="max")]
public class Max {
[XmlAttribute(AttributeName="missing")]
public string Missing { get; set; }
}
[XmlRoot(ElementName="source_code")]
public class Source_code {
[XmlAttribute(AttributeName="missing")]
public string Missing { get; set; }
}
[XmlRoot(ElementName="COMPO")]
public class COMPO {
[XmlElement(ElementName="alim_code")]
public string Alim_code { get; set; }
[XmlElement(ElementName="const_code")]
public string Const_code { get; set; }
[XmlElement(ElementName="teneur")]
public string Teneur { get; set; }
[XmlElement(ElementName="min")]
public Min Min { get; set; }
[XmlElement(ElementName="max")]
public Max Max { get; set; }
[XmlElement(ElementName="code_confiance")]
public string Code_confiance { get; set; }
[XmlElement(ElementName="source_code")]
public Source_code Source_code { get; set; }
}
Now you can use `XmlSerializer class to deserialize it :
XmlSerializer serializer = new XmlSerializer(typeof(List<COMPO>));
List<COMPO> compos = null;
using (var reader = new StringReader(xml))
{
compos = (List<COMPO>)serializer.Deserialize(reader);
}
EDIT:
In that case add another type for Table which would be :
[XmlRoot(ElementName="Table")]
public class Table {
[XmlElement(ElementName="COMPO")]
public List<COMPO> COMPO { get; set; }
}
and now the adjust the de-serialization code accordingly:
XmlSerializer serializer = new XmlSerializer(typeof(Table));
Table table = null;
using (var reader = new StringReader(xml))
{
compos = (Table)serializer.Deserialize(reader);
}
I would use Xml Linq. There are some items that can be null that you need to handle properly. See code below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Globalization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
IFormatProvider provider = CultureInfo.InvariantCulture;
List<Compo> compos = doc.Descendants("COMPO").Select(x => new Compo() {
alim_code = (int?)x.Element("alim_code"),
const_code = (int?)x.Element("const_code"),
teneur = (string)x.Element("teneur") == "" ? null : (decimal?)Convert.ToDecimal((string) x.Element("teneur"),provider),
min = (string)x.Element("min") == "" ? null : (decimal?)Convert.ToDecimal((string) x.Element("teneur"),provider),
max = (string)x.Element("max") == "" ? null : (decimal?)Convert.ToDecimal((string) x.Element("teneur"),provider),
code_confiance = (string)x.Element("code_confiance"),
source_code = (string)x.Element("source_code") == "" ? null : (int?)int.Parse(((string)x.Element("source_code")).Trim())
}).ToList();
}
}
public class Compo
{
public int? alim_code {get; set;}
public int? const_code {get; set;}
public decimal? teneur {get; set;}
public decimal? min {get; set;}
public decimal? max {get; set;}
public string code_confiance {get; set;}
public int? source_code { get; set; }
}
}
Working solution based on Ehsan-Sajjad proposal :)
[XmlRoot(ElementName = "min")]
public class Min
{
[XmlAttribute(AttributeName = "missing")]
public string Missing { get; set; }
[XmlText]
public string Value { get; set; }
}
[XmlRoot(ElementName = "max")]
public class Max
{
[XmlAttribute(AttributeName = "missing")]
public string Missing { get; set; }
[XmlText]
public string Value { get; set; }
}
[XmlRoot(ElementName = "source_code")]
public class Source_code
{
[XmlAttribute(AttributeName = "missing")]
public string Missing { get; set; }
[XmlText]
public string Value { get; set; }
}
[XmlRoot(ElementName = "COMPO")]
public class COMPO
{
[XmlElement(ElementName = "alim_code")]
public string Alim_code { get; set; }
[XmlElement(ElementName = "const_code")]
public string Const_code { get; set; }
[XmlElement(ElementName = "teneur")]
public string Teneur { get; set; }
[XmlElement(ElementName = "min")]
public Min Min { get; set; }
[XmlElement(ElementName = "max")]
public Max Max { get; set; }
[XmlElement(ElementName = "code_confiance")]
public string Code_confiance { get; set; }
[XmlElement(ElementName = "source_code")]
public Source_code Source_code { get; set; }
}
[XmlRoot(ElementName = "TABLE")]
public class TABLE
{
[XmlElement(ElementName = "COMPO")]
public List<COMPO> COMPO { get; set; }
}
private void ReadCompoWithSerializer()
{
string xmlFile = "test.xml";
string xml = File.ReadAllText(xmlFile);
XmlSerializer serializer = new XmlSerializer(typeof(TABLE));
TABLE table = null;
using (var reader = new StringReader(xml))
{
table = (TABLE)serializer.Deserialize(reader);
}
}
Inside Min/Max/Source_code objects Value contains the value when present (in that case Missing is null) otherwise Value is null (in that case Missing contain a string)
Working solution based on jdweng proposal
public class Compo {
public string alim_code { get; set; }
public string const_code { get; set; }
public string teneur { get; set; }
public string min { get; set; }
public string max { get; set; }
public string code_confiance { get; set; }
public string source_code { get; set; }
}
private void ReadCompoWithLinq() {
const string FILENAME = "test.xml";
XDocument doc = XDocument.Load(FILENAME);
List<Compo> compos = doc.Descendants("COMPO").Select(x => new Compo()
{
alim_code = (string)x.Element("alim_code"),
const_code = (string)x.Element("const_code"),
teneur = (string)x.Element("teneur"),
min = (x.Element("min").Attribute("missing") != null) ? null : (string)x.Element("min"),
max = (x.Element("max").Attribute("missing") != null) ? null : (string)x.Element("max"),
code_confiance = (string)x.Element("code_confiance"),
source_code = (x.Element("source_code").Attribute("missing") != null) ? null : (string)x.Element("source_code"),
}).ToList();
}
The min,max,source_code objects contains the string value when provided (in that case the attribute "missing is used in XML description) otherwise the string value is null.

Batch Data Extraction Script

apologies for the newbie question as I am very new to the world of programming etc.
I have a large XML file containing (see link below). It contains an ID number for legal entities (LEI) followed by the ID for their respective parent company.
Example from the XML file
The yellow is the entity LEI number and the green is the parent company LEI.
I would like to create some sort of batch script or GUI so I can enter a list of entity (green) LEI numbers and then given an output of all the corresponding parent LEI numbers.
Here is the file for anyone wondering: https://leidata.gleif.org/api/v1/concatenated-files/rr/20171025/zip
I am very inexperienced so I am not sure where to start.
Many thanks
Copy the entire file content to clipboard, then open your visual studio project and go to menu -> edit -> paste special -> paste as xml classes. This will generate a bunch of classes in the file you have currently open. Save this file. Then you use this code to load the file and return the loaded data:
public static RelationshipData LoadFile(string fileName)
{
var serializer = new XmlSerializer(typeof(Items));
RelationshipData data;
using (Stream r = new FileStream(filename, FileMode.Open))
{
data = (RelationshipData)serializer.Deserialize(r);
}
return data;
}
Now you should be able to access the file content using the properties of the returned data object. Haven't used this method with xml-data that uses prefixes, but I think this will be handled too...
With this data you should build up a dictionary or something like that that suits your needs that maps one ID to the other ID.
You should really start reading why and how this works...
NOT TESTED
I think I got most of the elements. You need to do some careful checking since there are a lot of nodes. I added a writer so I can use Beyond Compare to check if I got all the nodes. Fixed some errors. Still need some minor tweaks.
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 INPUT_FILENAME = #"c:\temp\test.xml";
const string OUTPUT_FILENAME = #"c:\temp\test1.xml";
static void Main(string[] args)
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.CheckCharacters = false;
XmlReader reader = XmlReader.Create(INPUT_FILENAME, settings);
XmlSerializer serializer = new XmlSerializer(typeof(RelationshipDataRelationshipData));
RelationshipDataRelationshipData relationshipDataRelationshipData = (RelationshipDataRelationshipData)serializer.Deserialize(reader);
XmlWriterSettings wSettings = new XmlWriterSettings();
wSettings.Indent = true;
XmlWriter writer = XmlWriter.Create(OUTPUT_FILENAME, wSettings);
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("rr", "http://www.gleif.org/data/schema/rr/2016");
ns.Add("gleif", "http://www.gleif.org/concatenated-file/header-extension/2.0");
serializer.Serialize(writer, relationshipDataRelationshipData, ns);
writer.Flush();
writer.Close();
}
}
[XmlRoot(ElementName = "RelationshipData", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public class RelationshipDataRelationshipData
{
[XmlElement("Header")]
public Header header { get; set; }
[XmlElement("RelationshipRecords", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public RelationshipRecords relationshipRecords { get; set; }
}
[XmlRoot(ElementName = "Header", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public class Header
{
[XmlElement("ContentDate")]
public DateTime ContentDate { get; set; }
[XmlElement("FileContent")]
public string FileContent { get; set; }
[XmlElement("RecordCount")]
public int RecordCount { get; set; }
[XmlElement("Extension", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public Extension extension { get; set; }
}
[XmlRoot(ElementName = "Extension", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public class Extension
{
[XmlElement(ElementName = "Sources", Namespace = "http://www.gleif.org/concatenated-file/header-extension/2.0")]
public Sourses sources { get; set; }
}
[XmlRoot(ElementName = "Sources", Namespace = "http://www.gleif.org/concatenated-file/header-extension/2.0")]
public class Sourses
{
[XmlElement("Source")]
public List<Source> source { get; set; }
}
[XmlRoot(ElementName = "Source", Namespace = "http://www.gleif.org/concatenated-file/header-extension/2.0")]
public class Source
{
[XmlElement("ContentDate")]
public DateTime ContentDate { get; set; }
[XmlElement("Originator")]
public string Originator { get; set; }
[XmlElement("RecordCount")]
public int RecordCount { get; set; }
}
[XmlRoot(ElementName = "RelationshipRecords", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public class RelationshipRecords
{
[XmlElement("RelationshipRecord")]
public List<RelationshipRecord> relationshipRecord { get; set; }
}
[XmlRoot(ElementName = "RelationshipRecord", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public class RelationshipRecord
{
[XmlElement("Relationship")]
public Relationship relationship { get; set; }
[XmlElement("Registration")]
public Registration registration { get; set; }
[XmlElement("Extension", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public Extension extension { get; set; }
}
[XmlRoot(ElementName = "Relationship", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public class Relationship
{
[XmlElement("StartNode")]
public Node StartNode { get; set; }
[XmlElement("EndNode")]
public Node EndNode { get; set; }
public string RelationshipType { get; set; }
[XmlElement("RelationshipPeriods")]
public RelationshipPeriods relationshipPeriods { get; set; }
public string RelationshipStatus { get; set; }
[XmlElement("RelationshipQualifiers")]
public RelationshipQualifiers relationshipQualifiers { get; set; }
}
[XmlRoot(ElementName = "Node", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public class Node
{
public string NodeID { get; set; }
public string NodeIDType { get; set; }
}
[XmlRoot(ElementName = "class RelationshipQualifiers", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public class RelationshipQualifiers
{
[XmlElement("RelationshipQualifier")]
public RelationshipQualifier relationshipQualifier { get; set; }
}
[XmlRoot(ElementName = "class RelationshipQualifier", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public class RelationshipQualifier
{
public string QualifierDimension { get; set; }
public string QualifierCategory { get; set; }
}
[XmlRoot(ElementName = "Registration", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public class Registration
{
public DateTime InitialRegistrationDate { get; set; }
public DateTime LastUpdateDate { get; set; }
public string RegistrationStatus { get; set; }
public DateTime NextRenewalDate { get; set; }
public string ManagingLOU { get; set; }
public string ValidationSources { get; set; }
public string ValidationDocuments { get; set; }
}
[XmlRoot(ElementName = "class RelationshipPeriods", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public class RelationshipPeriods
{
[XmlElement("RelationshipPeriod")]
public List<RelationshipPeriod> relationshipPeriod { get; set; }
}
[XmlRoot(ElementName = "class RelationshipPeriod", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public class RelationshipPeriod
{
[XmlElement("StartDate")]
public DateTime StartDate { get; set; }
[XmlElement("EndDate")]
public DateTime EndDate { get; set; }
[XmlElement("PeriodType")]
public string PeriodType { get; set; }
}
[XmlRoot(ElementName = "class RelationshipQuantifiers", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public class RelationshipQuantifiers
{
[XmlElement("RelationshipQuantifier")]
public List<RelationshipQuantifier> relationshipQuantifier { get; set; }
}
[XmlRoot(ElementName = "class RelationshipQuantifier", Namespace = "http://www.gleif.org/data/schema/rr/2016")]
public class RelationshipQuantifier
{
[XmlElement("MeasurementMethod")]
public DateTime MeasurementMethod { get; set; }
[XmlElement("QuantifierAmount")]
public decimal QuantifierAmount { get; set; }
[XmlElement("QuantifierUnits")]
public string QuantifierUnits { get; set; }
}
}

C#: JSON One to Many Data Parse - SSIS Script Component

Any help would be appreciated. I am trying to parse data from a JSON source file in SSIS (SQL Server Integration Services). I can parse through the data but am getting stuck on parsing the data where there is a 'one to many' relationship. There is a data entity repeated several times ("display_k") -
"responses":
[
{"display_k":"good","answer":null}
,{"display_k":"bad","answer":null}
,{"display_k":"general","answer":"The whole process was Easy. "}
,{"display_k":"would_buy_again","answer":true}
,{"display_k":"happy_with_customer_service","answer":null}
]
The full code is below:
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
using System.IO;
//using Newtonsoft.Json;
using System.Collections.Generic;
using System.Runtime.Serialization.Json;
using System.Text;
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
public override void CreateNewOutputRows()
{
var filePath = Connections.Connection.AcquireConnection(null).ToString();
using (var fileContents = new StreamReader(filePath))
while (fileContents.Peek() >= 0)
{
var record = fileContents.ReadLine();
var ser = new DataContractJsonSerializer(typeof(RootObject));
var memStream = new MemoryStream(UTF8Encoding.UTF8.GetBytes(record));
var root = ser.ReadObject(memStream) as RootObject;
//reviewables
var CustomerExperienceReview = root.customer_experience_reviews;
foreach (var CER in CustomerExperienceReview)
{
OutputBuffer.AddRow();
OutputBuffer.id = CER.id;
OutputBuffer.branchattribution = CER.branch_attribution;
OutputBuffer.reviewerfirstname = CER.reviewer.first_name;
OutputBuffer.reviewerid = CER.reviewer.id;
// Cannot get the output buffer to show the correct result:
OutputBuffer.responsesdisplaykey = string.Join(",", CER.responses.display_key);
}
}
}
public class Pagination
{
public int total_entries { get; set; }
public int current_page { get; set; }
public int total_pages { get; set; }
public int per_page { get; set; }
public object previous_page { get; set; }
public int next_page { get; set; }
}
public class Summary
{
public Pagination pagination { get; set; }
public int moving_window_size { get; set; }
public SortOrder sort_order { get; set; }
public List<object> sort_orders { get; set; }
}
public class Reviewer
{
public string first_name { get; set; }
public int id { get; set; }
}
public class Respons
{
public string display_key { get; set; }
public object answer { get; set; }
}
public class CustomerExperienceReview
{
public int id { get; set; }
public string branch_attribution { get; set; }
public List<Respons> responses { get; set; }
}
public class RootObject
{
public Summary summary { get; set; }
public List<CustomerExperienceReview> customer_experience_reviews { get; set; }
}
}
You have:
OutputBuffer.responsesdisplaykey = string.Join(",", CER.responses.display_key);
Based on the class definitions, CER.responses is a List<Respons>. If you want a comma-separated list of display keys, you'll need to project the Respons objects to strings.
OutputBuffer.responsesdisplaykey = string.Join(",", CER.responses.Select(r=>r.display_key));

Categories