LinqToXml help needed for weird iTunes Export XML code - itunes

Itunes is giving me an export xml structure that I’m not used to work with (see xml code):
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<dict>
<key>68768</key>
<dict>
<key>Track ID</key><integer>68768</integer>
<key>Name</key><string>ABBA Medley</string>
<key>Artist</key><string>Party All Stars</string>
<key>Disc Number</key><integer>1</integer>
<key>Track Number</key><integer>17</integer>
<key>Persistent ID</key><string>B121116C66BE0F4B</string>
</dict>
<key>68781</key>
<dict>
<key>Track ID</key><integer>68781</integer>
<key>Name</key><string>Afflitto</string>
<key>Artist</key><string>Fiocco</string>
<key>Disc Number</key><integer>1</integer>
<key>Track Number</key><integer>9</integer>
<key>Persistent ID</key><string>5D09837999591307</string>
</dict>
<key>68793</key>
<dict>
<key>Track ID</key><integer>68793</integer>
<key>Name</key><string>Agadou</string>
<key>Artist</key><string>Saradossa Band</string>
<key>Disc Number</key><integer>1</integer>
<key>Track Number</key><integer>4</integer>
<key>Persistent ID</key><string>4DFFF3C3498C02B4</string>
</dict>
</dict>
</dict>
</plist>
My goal is to get following array when possible achieved with LinqToXml in c#:
17 ABBA Medley B121116C66BE0F4B
9 Afflitto 5D09837999591307
4 Agadou 4DFFF3C3498C02B4
Is this even possible ?
I wrote some basic c# code but a lightning bolt strikes me on the way...
XDocument doc = XDocument.Load(iTunesXmlFile);
var linqtoxml = from node in doc.Descendants("dict") select node; // ooo Boy :(
foreach (var dict in linqtoxml)
{
// and lightning bolt again...
}
Ps: As obvious, I can't change the xml structure.
Pps: I know, this is a cleanup iTunes XML version, but the basic concept is presented as case study.

The best solution that I could think of was following :
List<Track> MyTrackList = new List<Track>();
public class Track
{
public int TrackNumber { get; set; }
public string Name { get; set; }
public string PersistentID { get; set; }
}
XDocument doc = XDocument.Load(#"thexmlfile.xml");
var linqtoxml = from element in doc.Descendants("plist").Descendants("dict").Descendants("dict").Descendants("dict")
select element;
foreach (var dict in linqtoxml)
{
try
{
string myTrackNumber = dict.Descendants("key").Where(key => key.Value == "Track Number").Select(x => x.NextNode).SingleOrDefault().ToString();
string myName = dict.Descendants("key").Where(key => key.Value == "Name").Select(x => x.NextNode).SingleOrDefault().ToString();
string myPersistentID = dict.Descendants("key").Where(key => key.Value == "Persistent ID").Select(x => x.NextNode).SingleOrDefault().ToString();
MyTrackList.Add(new Track
{
TrackNumber = myTrackNumber,
Name = myName,
PersistentID = myPersistentID
});
}
catch (Exception)
{
// Something wasn't set, so skip the node ...
}
}

Related

Saving and Loading file definitions while using <generic> version of FileEngine

I've successfully use the SaveToXml and LoadFromXml methods on the ClassBuilder class to store and retrieve the file definitions while using the Standard version of the File Helper Engine.
However I'd really prefer to use the generic version of the File Helper Engine. In other words I'd like to instatiate the engine like so:
var eng = new DelimitedFileEngine<OutputClass>(params....);
OutputClass[] records = eng.ReadFile("Sample.csv");
So my results are strongly typed and not just an array of objects.
Does this save and load functionality exist for the generic file helper engine?
Sure, it works exactly as you'd expect.
[DelimitedRecord("|")]
public class OutputClass
{
public string First { get; set; }
public string Second { get; set; }
}
class Program
{
static void Main(string[] args)
{
var eng = new DelimitedFileEngine<OutputClass>();
// To read from a file use ReadFile()
//OutputClass[] records = eng.ReadFile("Sample.csv");
// Or to read from a string use ReadString()
OutputClass[] records = eng.ReadString("First|Second");
Debug.Assert(records.Length == 1);
Debug.Assert(records[0].First == "First");
Debug.Assert(records[0].Second == "Second");
Console.WriteLine("All OK");
Console.ReadKey();
}
}
Edit:
Based on your comment below, you want to map the results from your XML class to a concrete C# object. The easiest way is to use read into a DataTable and map the fields to the C# object.
var cb = new DelimitedClassBuilder(nameof(OutputClass), "|");
cb.AddField("First", typeof(string));
cb.AddField("Second", typeof(string));
var xmlString = cb.SaveToXmlString();
var outputClass = DelimitedClassBuilder.LoadFromXmlString(xmlString);
var eng = new FileHelperEngine(outputClass.CreateRecordClass());
OutputClass[] records = eng
.ReadStringAsDT("First|Second")
.Rows.OfType<DataRow>()
.Select(x => new OutputClass() {
First = x.Field<string>("First"),
Second = x.Field<string>("Second")
})
.ToArray();

Make Swagger output the description of an IRouteConstraint

I have a route with a custom IRouteConstraint.
Swagger generates the parameter section but the description field is always empty. For other parameters I get the description from the XML comments correctly.
The only workaround I found so far is adding a Operation filter and set the description there for the
foreach ( var parameter in operation.Parameters.OfType<NonBodyParameter>() )
{
if (parameter.Name == RouteConstraint.Name)
{
parameter.Description = GetConstraintDescription();
}
Any way to instruct swagger to get the description from the XML comments for IRouteConstraints.
I'm afraid that the only "easy way" is what you already implemented (an Operation filter)
I was looking at the XML description that your project outputs
<?xml version="1.0"?>
<doc>
<assembly>
<name>SwaggerWeb</name>
</assembly>
<members>
<member name="M:SwaggerWeb.Controllers.ValuesController.Get(System.Int32)">
<summary> Get by ID </summary>
<param name="id">The value ID</param>
<returns></returns>
</member>
<member name="T:SwaggerWeb.SectorRouteConstraint">
<summary> Sector constraint </summary>
</member>
</members>
</doc>
You can try make your filter a bit more generic and fish the description out of the XML, but other than that I do not see any other way.
My current solution is this class, based on the Swashbuckle XmlCommentsOperationFilter.
public class RouteConstraintXmlDocsOperationFilter:IOperationFilter
{
private readonly XPathNavigator _xmlNavigator;
private const string MemberXPath = "/doc/members/member[#name='{0}']";
private const string SummaryXPath = "summary";
public RouteConstraintXmlDocsOperationFilter(string filePath)
{
XPathDocument xmlDoc = new XPathDocument(filePath);
_xmlNavigator = xmlDoc.CreateNavigator();
}
public void Apply(Operation operation, OperationFilterContext context)
{
ApplyConstraintsXmlToActionParameters(operation.Parameters, context.ApiDescription);
}
private void ApplyConstraintsXmlToActionParameters(IList<IParameter> parameters, ApiDescription apiDescription)
{
var nonBodyParameters = parameters.OfType<NonBodyParameter>();
foreach (var parameter in nonBodyParameters)
{ // Check for a corresponding action parameter?
var actionParameter = apiDescription.ParameterDescriptions.FirstOrDefault(p =>
parameter.Name.Equals(p.Name, StringComparison.OrdinalIgnoreCase));
if (actionParameter == null) continue;
if (!actionParameter.RouteInfo.Constraints.Any()) continue;
var constraintType = actionParameter.RouteInfo.Constraints.FirstOrDefault().GetType();
var commentIdForType = XmlCommentsIdHelper.GetCommentIdForType(constraintType);
var constraintSummaryNode = _xmlNavigator
.SelectSingleNode(string.Format(MemberXPath, commentIdForType))
?.SelectSingleNode(SummaryXPath);
if (constraintSummaryNode != null)
{
parameter.Description = XmlCommentsTextHelper.Humanize(constraintSummaryNode.InnerXml);
}
}
}
}
To set it up:
services.AddSwaggerGen(o =>
{
var fileName = GetType().GetTypeInfo().Module.Name.Replace(".dll", ".xml").Replace(".exe", ".xml");
o.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, fileName));
o.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
o.OperationFilter<RouteConstraintXmlDocsOperationFilter>(Path.Combine(AppContext.BaseDirectory, fileName));
})

Springfox API docu with xml response- Workaround for #XmlAttribute

I have some issues setting up Springfox for usage in Swagger UI.
I got it working in general, but the XML examples which are created are partly wrong because some xml annoations are not considered by springfox.
I have these models:
#XmlRootElement(name = "ApplicatorUnits")
#XmlAccessorType(XmlAccessType.FIELD)
public class EHDTOApplicatorUnits
{
#XmlElement(name = "UnitGroup")
private List<EHDTOUnitGroup> ehdtoUnitGroups;
public List<EHDTOUnitGroup> getEhdtoUnitGroups()
{
return ehdtoUnitGroups;
}
public void setEhdtoUnitGroups(List<EHDTOUnitGroup> ehdtoUnitGroups)
{
this.ehdtoUnitGroups = ehdtoUnitGroups;
}
}
#XmlRootElement(name = "UnitGroup")
#XmlAccessorType(XmlAccessType.FIELD)
public class EHDTOUnitGroup
{
#XmlAttribute(name = "name")
private String name;
#XmlElement(name = "unit")
private List<EHDTOUnit> ehdtoUnits;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public List<EHDTOUnit> getEhdtoUnits()
{
return ehdtoUnits;
}
public void setEhdtoUnits(List<EHDTOUnit> ehdtoUnits)
{
this.ehdtoUnits = ehdtoUnits;
}
}
You see some usages of #XmlAttribute, #XmlRootElement and #XmlElement.
After adding jackson jaxb annotation introspectors (I tried different ways) the XML was partly correct, as the #XmlAttribute annotations have been considered after this step:
JaxbAnnotationModule jaxbAnnotationModule = new JaxbAnnotationModule();
jaxbAnnotationModule.setPriority(Priority.PRIMARY);
mapper.registerModule(jaxbAnnotationModule);
/*AnnotationIntrospector primary = new JacksonAnnotationIntrospector();
AnnotationIntrospector secondary = new XmlJaxbAnnotationIntrospector(mapper.getTypeFactory()); //new JaxbAnnotationIntrospector(mapper.getTypeFactory());
AnnotationIntrospector pair = new AnnotationIntrospectorPair(primary,secondary);
mapper.setAnnotationIntrospector(pair);*/
However, the other 2 are still ignored, resulting in this example xml:
<?xml version="1.0"?>
<EHDTOApplicatorUnits>
<UnitGroup>
<name>string</name>
<unit>
<id>1</id>
<name>string</name>
<unitSystem>string</unitSystem>
</unit>
</UnitGroup>
</EHDTOApplicatorUnits>
While the real output of the API is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ApplicatorUnits>
<UnitGroup name="coefTempErrorSpan">
<unit>
<id>1</id>
<name>% span /10K</name>
<unitSystem>SI</unitSystem>
</unit>
<unit>
<id>2</id>
<name>% span /50F</name>
<unitSystem>SI</unitSystem>
</unit>
</UnitGroup>
...
</ApplicatorUnits>
You see the different naming of the root element, and also the "name" of the UnitGroup which is not an attribute in the example xml but an own child-node.
I found I can use #ApiModel(value = "ApplicatorUnits") as workaround for #XmlRootElement but what about #XmlAttribute, is there also a workaround? #ApiModelProperty does not seem to work for this case. Does somebody have an idea how to workaround this issue?

Dropdownlist just retrieving objects and dynamically show in Ajax Text box

So Im starting as a new ASP Developer (been using Java for a bit, but now project demands ASP), so bare a little with my ignorance :( .
What I am trying to do is create a dynamic dropdown list from a table I got in SQL SERVER and depending on what you choose on the list show the information on the txt editor (In theory shouldnt be so hard, but since I just started, it just does not seem so easy). I created the entity for it, the data for it and the bussiness logic for it and the interconnection (It is already reading and retrieving from the DB, but just a list). Here is, what I have doing so hard.
Entity.Messages
public class Messages
{
public int id { get; set; }
public string title { get; set; }
public string subject { get; set; }
public string body { get; set; }
public string createdBy { get; set; }
public Messages()
{
id = 0;
title = "";
subject = "";
body = "";
createdBy = "";
}
public Messages(int idMessages)
{
idMessages = id;
title = "";
subject = "";
body = "";
createdBy = "";
}
}
}
Data.Messages
public class Messages : Data
{
public Messages() : base()
{
}
public List<Entity.Messages> GetAll()
{
List<Entity.Messages> message = new List<Entity.Messages>();
//SQL Command para llamar el stored procedure
SqlCommand comando = new SqlCommand("dbo.[Messages_GetAll]", base.Db);
//Ejecuta consulta
DataTable dtItem = base.Execute(comando);
//Transforma el Datatable en una lista de proyectos.
foreach (DataRow dr in dtItem.Rows)
message.Add(GetFromDataRow(dr));
return message;
}
public Entity.Messages GetById(int id)
{
Entity.Messages m = new Entity.Messages();
//SQL Command para llamar el stored procedure
SqlCommand comando = new SqlCommand("dbo.[Messages_GetById]", base.Db);
//parametros del store procedure
SqlParameter spKey = new SqlParameter("#Id", System.Data.SqlDbType.Int);
spKey.Value = id;
comando.Parameters.Add(spKey);
//Ejecuta consulta
DataTable dt = base.Execute(comando);
if (dt.Rows.Count > 0)
m = GetFromDataRow(dt.Rows[0]);
return m;
}
private ASF.HC.JobApplication.Entity.Messages GetFromDataRow(DataRow dr)
{
Entity.Messages m = new Entity.Messages();
m.id = dr["Id"] == DBNull.Value ? -1 : int.Parse(dr["Id"].ToString());
m.title = dr["Title"] == DBNull.Value ? "" : dr["Title"].ToString();
m.subject = dr["Subject"] == DBNull.Value ? "" : dr["Subject"].ToString();
m.body = dr["Body"] == DBNull.Value ? "" : dr["Body"].ToString();
m.createdBy = dr["createdBy"] == DBNull.Value ? "" : dr["createdBy"].ToString();
return m;
}
}
BO.Messages
public class Messages
{
public Entity.Messages GetByID(int id)
{
Data.Messages oMessage = new Data.Messages();
return oMessage.GetById(id);
}
public List<Entity.Messages> GetAll()
{
Data.Messages oMessage = new Data.Messages();
return oMessage.GetAll();
}
And where Im trying to display it, I see the DropDownList and well I see the List with the objects, but I want to display is the title.
MEssage.aspx
<%# Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Messages.aspx.cs" Inherits="ASF.HC.JobApplication.Admin.Messages" %>
<%# Register assembly="AjaxControlToolkit" namespace="AjaxControlToolkit" tagprefix="ajaxToolkit" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Messages</h2>
<asp:ScriptManager ID="ScriptManager2" runat="server"></asp:ScriptManager>
<legend>Pick the template to use:</legend>
<asp:dropdownlist id ="ddlTemplate" runat ="server" Height="38px" Width="397px">
<asp:listitem value ="1"> Juan Valdez </asp:listitem >
<asp:listitem Value ="2"> Querido bebe</asp:listitem>
</asp:dropdownlist >
<p> </p>
<asp:TextBox ID ="txtDetails" runat="server" Width="600px" Height="300px" Visible="true" ></asp:TextBox>
<ajaxToolkit:HtmlEditorExtender ID="TextBox1_HtmlEditorExtender" runat="server" TargetControlID="txtDetails"
EnableSanitization="false" DisplaySourceTab="true" >
</ajaxToolkit:HtmlEditorExtender>
</asp:Content>
Messages.aspx.cs
public partial class Messages : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
loadList();
}
public void loadList()
{
BO.Messages template = new BO.Messages();
ddlTemplate.DataSource = template.GetAll();
ddlTemplate.DataBind();
}
}
}
Im trying to go step by step, but first I wanna see the value and not the objects, and according to what I choose to see the Body field Displayed on the text field on Ajax. But first, and most importantly, show the title field on the dropdownlist :(
EDIT: So I got to show the Title instead of all the object. But now, do you guys know any way to as soon as I choose the title, to have it displayed on the Text from ajax? Like dynamically? Any point would be greatly appreciated.
Any help or pointer would be greatly appreciated.
Thanks!
Apparently, all I needed was to map the datavalue and datatext field.

Object serialization duplicating nodes

Can anyone tell me why my output is duplicating the 'FirstNode'?
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml.Serialization;
class Program
{
static void Main(string[] args)
{
Root root = new Root();
FirstNode firstNode = new FirstNode();
firstNode.Data = "DATA";
root.FirstNode.Add(firstNode);
XmlSerializer s = new XmlSerializer(typeof(Root));
StringWriter sw = new StringWriter();
s.Serialize(sw, root);
string serializedXml = sw.ToString();
Console.WriteLine(serializedXml);
Console.ReadKey();
}
}
public class Root
{
List<FirstNode> firstNode = new List<FirstNode>();
public List<FirstNode> FirstNode
{
get { return firstNode; }
set { firstNode = value; }
}
}
public class FirstNode
{
string data;
public string Data
{
get { return data; }
set { data = value; }
}
}
OUTPUT
<Root>
<FirstNode>
<FirstNode>
<Data>DATA</Data>
</FirstNode>
</FirstNode>
</Root>
Expected Output
<Root>
<FirstNode>
<Data>DATA</Data>
</FirstNode>
</Root>
Well if you look you have a Property Named FirstNode where you are storing your list, and inside the List you are storing FirstNode object...
If you want to see it just change the name of the property FirstNode in the class Root to Nodes and you will see a differnet output
<Root>
<Nodes>
<FirstNode>
<Data>DATA</Data>
</FirstNode>
</Nodes>
</Root>
The Root tag appears because is the Object Type, as well as the FirsNode , then you have tags like Data and Nodes( in my case) because are the serialized properties of these classes
Think it is this line:
root.FirstNode.Add(firstNode);
You are adding a first node to a first node and therefore getting two layers of first node.
You want:
[XmlElement("FirstNode")]
public List<FirstNode> FirstNode
{
get { return firstNode; }
set { firstNode = value; }
}
This will only add <FirstNode> for the content items - not the list itself. You might also want to look at [XmlArray] / [XmlArrayItem] if you want finer control.