So, I have the following function that takes in a Dictionary of Users and ControlNumbers and outputs it to XML. Found some LINQ online that did this very well; but I have one small problem.
static Dictionary<string, User> UserClassDict = new Dictionary<string, User>();
static void DictionaryToXML(Dictionary<string,User> UserClassDict)
{
XElement el = new XElement("root", UserClassDict.Select(kv => new XElement(kv.Key, kv.Value.ControlNumber
)));
}
The XML looks like this:
<root>
<adolan>792365</adolan>
<afeazell>791964</afeazell>
<amsmith>790848</amsmith>
<asnyder>790948789358</asnyder>
</root>
But as you can see, the ControlNumbers are generally 6 digits long (HOWEVER this is not always the case). What I would like to happen is something similar to this.
<root>
<adolan>
<controlNumbers>123456</controlNumbers>
</adolan>
<asnyder>
<controlNumbers>222111</controlNumbers>
<controlNumbers>333222</controlNumbers>
</asnyder>
</root>
Eventually I will have the program read this XML file at start up and populate the Dictionary so this XML will eventually get pretty large. Any ideas would be helpful.
Try this
XElement el = new XElement("root",
UserClassDict.Select(kv => new XElement(kv.Key,
kv.Value.ControlNumbers.Select(num => new XElement("controlNumbers", num))))
);
I don't fully understand how 2 or more control numbers are represented in your dictionary, but if you want to do some more complex xlm generation, you can change your lambda so that it invokes a method.
kv => new XElement(kv.Key, kv.Value.ControlNumber)
would change to
kv => BuildXMLElement(kv)
and you can implement BuildXMLElement to build the element as you like
Change your el to
XElement el = new XElement("root", UserClassDict.
Select(kv => new XElement(kv.Key,
from it in kv.Value.ControlNumber
select new XElement("controlNumbers", it)
)));
The above LINQ query will create multiple controlNumbers tags
To concatenate, use
XElement el = new XElement("root", UserClassDict.
Select(kv => new XElement(kv.Key,
String.Join(",", kv.Value.ControlNumber.ToArray())
)));
Related
Hello I have a list of object i want to convert into an xml. Here is what the final xml should look like.
<ArrayOfTweet>
<Tweet>
<Photos>
<Photo>
<PhotoHeight>FOO</PhotoHeight>
<PhotoUri>a/random/ur/path</PhotoUri>
<PhotoWidth>923</PhotoWidth>
<SourcePhotoUri>a/random/path</SourcePhotoUri>
</Photo>
</Photos>
<ProfileImage>a/random/path</ProfileImage>
<ScreenName>FOO</ScreenName>
<Text>some text</Text>
<TweetId>1234</TweetId>
<UserId>1234</UserId>
<Username>BAR</Username>
</Tweet>
<Tweet>
<Photos>
<Photo>
<PhotoHeight>FOO</PhotoHeight>
<PhotoUri>a/random/ur/path</PhotoUri>
<PhotoWidth>923</PhotoWidth>
<SourcePhotoUri>a/random/path</SourcePhotoUri>
</Photo>
</Photos>
<ProfileImage>a/random/path</ProfileImage>
<ScreenName>FOO</ScreenName>
<Text>some text</Text>
<TweetId>1234</TweetId>
<UserId>1234</UserId>
<Username>BAR</Username>
</Tweet>
</ArrayOfTweet>
I have converted each of the objects in the list into an xml string like so
//TweetList is the list of tweet objects
List<string> xmlStringTweetList = new List<string>();
foreach (var tl in TweetList)
{
xmlStringTweetList.Add(toXML(tl));
}
private string toXML(Tweet t)
{
var stringwriter = new System.IO.StringWriter();
var serializer = new XmlSerializer(t.GetType());
serializer.Serialize(stringwriter, t);
return stringwriter.ToString();
}
I tried converting that list into the format above using
XElement xmlElements = new XElement("ArrayOfTweet", xmlStringTweetList.Select(i => new XElement("Tweet", i)));
But there is the extra <Tweet></Tweet> That i dont need. Is there a way of doing this?
I made a fiddle here that illustrates a way to serialize your object all at once, instead of piecing strings together.
I suspect your extra <Tweet></Tweet> is because of a null or empty value in the list, because I am not experiencing it in my test above.
I think XElement xmlElements = new XElement("ArrayOfTweet", xmlStringTweetList.Select(i => XElement.Parse(i))); should do.
I have a relatively simple Winforms application and am using C#. I have been using LINQ (system.xml.linq) ... I have successfully been saving textbox items (single items) to my xml file.
I have a listBox of items and would like that list to be saved as child nodes in the xml file. So my xml final output would look something like this:
<items>
<centerName>My Center</centerName>
<poc>Mr. Jones</poc>
<servicedUnits>
<state>Texas</state>
<state>Iowa</state>
<state>Hawaii</state>
</servicedUnits>
</items>
I have this code that I have been using to write to XML:
private void writeFCATSettings()
{
XDocument doc = new XDocument
(
new XElement
("Items",
new XElement("textBoxCenterName", textBoxCenterName.Text),
new XElement("textBoxContactFirstName", textBoxContactFirstName.Text),
new XElement("servicedUnits", "<listBoxServicedUnits.Items>")
)
);
doc.Save(#"Settings.xml");
}
I am pretty sure I need to put code to iterate through the listbox between the brackets .
And lastly..I found this code that does iterate through my list...but if I just paste it into the space ... it doesn't work.
XElement servicedUnits = new XElement("ServicedUnits");
foreach (var item in listBoxServicedUnits.Items)
{
servicedUnits.Add(new XElement("unit", item));
}
XDocument document = new XDocument();
document.Add(servicedUnits);
Thank you for any ideas...
ER
Assuming your listbox's items are strings you could do this:
private void writeFCATSettings()
{
XDocument doc = new XDocument(
new XElement("Items",
new XElement("textBoxCenterName", textBoxCenterName.Text),
new XElement("textBoxContactFirstName", textBoxContactFirstName.Text),
new XElement("servicedUnits",
listBoxServicedUnits
.Items
.OfType<string>()
.Select(item => new XElement("unit", item)))));
doc.Save(#"Settings.xml");
}
Or, if they are not strings or they are mixed types you could do this:
private void writeFCATSettings()
{
XDocument doc = new XDocument(
new XElement("Items",
new XElement("textBoxCenterName", textBoxCenterName.Text),
new XElement("textBoxContactFirstName", textBoxContactFirstName.Text),
new XElement("servicedUnits",
listBoxServicedUnits
.Items
.OfType<Object>()
.Select(x => TransformYouObjectToAStringHere)
.Select(item => new XElement("unit", item)))));
doc.Save(#"Settings.xml");
}
I have an XML like this:
<msg action="getDetails" class="2">
<stamps msgtime="4/15/2014" ltq="2014-04-15">
<dat la="get" />
</stamps>
</msg>
How can I retrieve Dictionary of all the attributes and their corresponding values? The expected output should look this:
action - getDetails
class - 2
msgtime - 4/15/2014
ltq - 2014-04-15
la - get
I can get it work for a particular level, but not for all child elements.
var xDoc = XDocument.Load("path");
var attributes = xDoc.Descendants()
.SelectMany(x => x.Attributes())
.ToDictionary(x => x.Name.LocalName, x => (string)x);
Have you tried using Descendants?
It would allow you to get an IEnumerable<XElement> of all of the elements, getting the attributes is something you have already done it looks like.
Use Linq-to-XML it is much easier to get the attribute and their values.
Sample usage
XElement cust = new XElement("PhoneNumbers",
new XElement("Phone",
new XAttribute("type", "home"),
"555-555-5555"),
new XElement("Phone",
new XAttribute("type", "work"),
"555-555-6666")
);
IEnumerable<XElement> elList =
from el in cust.Descendants("Phone")
select el;
foreach (XElement el in elList)
Console.WriteLine((string)el.Attribute("type"));
I have a XML document that looks like this:
<Person>
<LastName>LastName1</LastName>
<FirstName>FirstName1</FirstName>
<MiddleName>MiddleName1</MiddleName>
</Person>
Originally I had a method to create this structure like below:
public XElement ToXML()
{
return new XElement("Person",
new XElement(this.LastName.ToXML()),
new XElement(this.FirstName.ToXML()),
new XElement(this.MiddleName.ToXML()));
}
The problem is there are a lot of other nodes other than just Person that use the Name values within the root. So what I tried doing was refactoring out the FirstName, LastName, and MiddleName elements to be in their own reusable class with a ToXMLDoc() method that returns those elements as a XDocument instead of a XElement (since the root will be dictated by the class needing the name children; might be Person, Employee, etc.)
This is what my new ToXMLDoc returns:
return new XDocument(new XElement(this.LastName.ToXML()),
new XElement(this.FirstName.ToXML()),
new XElement(this.MiddleName.ToXML()));
My problem is I want to now add this content to within the root XElement added by my Person class. I tried doing something like below, but I'm not using the constructor properly and getting a "Ambiguous constructor reference" error.
return new XElement("Person",
foreach (XElement xe in NameType.ToXMLDoc().Nodes())
{
new XElement(xe.Value);
}
);
How can I take the contents from the ToXMLDoc() method and add them to the XElement node being created for Person? Any help is appreciated, thanks!
You're currently trying to embed a foreach loop within a constructor call. That's not going to work - but it's actually pretty simple:
return new XElement("Person", NameType.ToXMLDoc()
.Nodes()
.Select(xe => new XElement(xe.Value));
Are you sure you don't just want to copy the elements wholesale though? In which case it would just be:
return new XElement("Person", NameType.ToXMLDoc().Nodes());
I would return an XElement, rather than an XDocument
This way you could just do:
new XElement("Person", NameType.ToXMLDoc())
I am preferring LINQ to XML over XMLReader because it feels much easier to use. However, I know I'm doing it wrong somewhere. I'm not looking for faster execution or anything, just a cleaner rewrite. I can't tell if it'll fit cleanly into the from foo in bar where foo is some condition form, but there's got to be a cleaner way.
I am not using anonymous objects here and instead outputting as strings because I am excluding some code which will adapt the data into existing objects - however, it is kind of a messy structure currently which I need to refactor.
My XML looks like this.
<?xml version="1.0" encoding="utf-8" ?>
<entity name="Something">
<Component type="RenderComponent">
<Material type="string">fur</Material>
<Reflectiveness type="int">678</Reflectiveness>
</Component>
<Component type="HealthComponent">
<Sound type="int">60</Sound>
</Component>
</entity>
And my code:
static void Main(string[] args)
{
XDocument xdoc = XDocument.Load(#"..\..\XMLFile1.xml");
List<string> comNames = new List<string>();
Dictionary<string, string> paramValues = new Dictionary<string, string>();
List<string> paramTypes = new List<string>();
foreach (XElement e in xdoc.Root.Elements("Component"))
{
string type = e.Attribute("type").Value;
foreach(XElement param in e.Elements())
{
paramValues.Add(param.Name.LocalName, param.Value);
paramTypes.Add(param.Attributes().First().Value);
}
Console.WriteLine(" \n\n\n");
comNames.Add(type);
}
}
Giving outputs of:
comNames - RenderComponent, HealthComponent
paramValues - (Material, fur), (Reflectiveness, 678), (Sound, 60)
paramTypes - string, int, int
If it clears it up somewhat, the general layout of the file:
Root node entity with a name attribute (forgot in this example)
n Component nodes with type attributes
Each Component node has a number of child nodes which have names, a type attribute, and a value.
I think, you can do something like this:
XDocument xdoc = XDocument.Load(#"..\..\XMLFile1.xml");
var data =
xdoc.Root.Elements("Component")
.SelectMany(e => new
{
type = e.Attribute("type").Value,
paramValues = e.Elements()
.Select(x => new KeyValuePair<string,
string>
(x.Name.LocalName,
x.Value)),
paramType = e.Elements()
.Select(x => x.Attributes().First()
.Value)
});
List<string> comNames = data.Select(x => x.type).ToList();
List<string> paramTypes = data.Select(x => x.paramType).ToList();
Dictionary<string, string> paramValues = data.Select(x => x.paramValues)
.ToDictionary(x => x.Key,
x => x.Value);
But honestly, I think your original code is just fine.