Creating xml with xdocument - c#

I want create xml file of this structure:
<Devices>
<Device Number="58" Name="Default Device" >
<Functions>
<Function Number="1" Name="Default func" />
<Function Number="2" Name="Default func2" />
<Function Number="..." Name="...." />
</Functions>
</Device>
</Devices>
Here's my code:
document.Element("Devices").Add(
new XElement("Device",
new XAttribute("Number", ID),
new XAttribute("Name", Name),
new XElement("Functions")));
Each object "device" have List<> of "functions", how can i add "functions" to xml???

Each object "device" have List<> of "functions", how can i add "functions" to xml???
Really easily - LINQ to XML makes this a doddle:
document.Element("Devices").Add(
new XElement("Device",
new XAttribute("Number", ID),
new XAttribute("Name", Name),
new XElement("Functions",
functions.Select(f =>
new XElement("Function",
new XAttribute("Number", f.ID),
new XAttribute("Name", f.Name))))));
In other words, you just project your List<Function> to an IEnumerable<XElement> using Select, and the XElement constructor does the rest.

document.Element("Devices").Add(
new XElement("Device",
new XAttribute("Number", ID),
new XAttribute("Name", Name),
new XElement("Functions", from f in functions select new XElement("Function", new XAttribute("Number", f.Number), new XAttribute("Name", f.Name)))));
functions would be your list of functions.

Related

Using XML to Linq to Iterate an ICollection

I have a collection of objects that I need to iterate over and pull data out to create an XML file. I am trying to use XML Linq to do so, but it appears I am not grasping the concept. Here is my code:
string jSonProducts = File.ReadAllText(settings.productJsonConfig.JSONProductFilePath);
ICollection<ProductSearchModel> prods = null;
prods = JsonConvert.DeserializeObject<ICollection<ProductSearchModel>>(jSonProducts);
foreach (ProductSearchModel prod in prods)
{
var xmlNode =
new XElement("Feed",
new XAttribute("xmlns", settings.bvXMLConfig.xmlns),
new XAttribute("name", settings.bvXMLConfig.xmlName),
new XAttribute("incrmental", settings.bvXMLConfig.xmlIncremental),
new XAttribute("extractDate",DateTime.UtcNow),
new XElement("Products"),
new XElement("Product"),
new XElement("ExternalId", prod.SKU),
new XElement("Name", prod.Description.Name),
new XElement("Description", prod.Description.Description),
new XElement("BrandExternalID",prod.Properties.Brand.FeedName),
new XElement("CategoryExternalId"), //look up the category here by sku
new XElement("ModelNumbers"),
new XElement("ModelNumber",prod.SKU),
new XElement("ManufacturingPartNumbers"),
new XElement("ManufacturingPartNumber",prod.SKU),
new XElement("UPCs"),
new XElement("UPC", prod.UPC),
new XElement("Attributes"),
new XElement("Attribute"),
new XAttribute("id","BV_FE_FAMILY"),
new XElement("Value")
);
}
I am trying to create the following XML:
<?xml version="1.0" encoding="utf-8"?>
<Feed xmlns="http://www.bazaarvoice.com/xs/PRR/ProductFeed/14.7" name="LifetimeProducts" incremental="false" extractDate="2017-10-20T13:21:41">
<Products>
<Product>
<ExternalId>12345</ExternalId>
<Name>Product 1</Name>
<Description>
<![CDATA[Proudct Description]]></Description>
<BrandExternalId>Brandx</BrandExternalId>
<CategoryExternalId>Category</CategoryExternalId>
<ProductPageUrl><![CDATA[http://something.com]]></ProductPageUrl>
<ImageUrl>http://image.com</ImageUrl>
<ModelNumbers>
<ModelNumber>12345</ModelNumber>
</ModelNumbers>
<ManufacturerPartNumbers>
<ManufacturerPartNumber>12345</ManufacturerPartNumber>
</ManufacturerPartNumbers>
<Attributes>
<Attribute id="BV_FE_FAMILY">
<Value>Family</Value>
</Attribute>
<Attribute id="BV_FE_FAMILY">
<Value>Family2</Value>
</Attribute>
<Attribute id="BV_FE_FAMILY">
<Value>Family 3</Value>
</Attribute>
</Attributes>
</Product>
</Products>
I need only one feed node (as the root) and then have the products nodes under this one. This doesn't seem to be working and this is my first time really working with LINQ and XML. The code appears to be pulling the data into the nodes, but with the foreach loop I think I am doing this wrong. What do I need to tweak to get this?
Thanks.
In C#, you pass arguments to methods by putting them inside the parentheses. For example, this won't work:
double sqrt = Math.Sqrt(),
1234;
You do it like this:
double sqrt = Math.Sqrt(1234);
You're also trying to create a new root node all over again for every item in the collection. Instead, you want one root node, which contains multiple children.
So your code should probably look something like this. Bear in mind that I don't have your settings or your ProductSearchModel class, so I haven't tested this. I'm relying on the assumption that your indentation, at least, reflects your intent, and the names of the elements (ModelNumbers/ModelNumber etc.) seem to bear that out.
The guts of the loop look similar to your code at first glance, but pay close attention to the parentheses. They make all the difference in the world.
var xFeed =
new XElement("Feed",
new XAttribute("xmlns", settings.bvXMLConfig.xmlns),
new XAttribute("name", settings.bvXMLConfig.xmlName),
new XAttribute("incrmental", settings.bvXMLConfig.xmlIncremental),
new XAttribute("extractDate", DateTime.UtcNow)
);
var xProducts = new XElement("Products");
xFeed.Add(xProducts);
foreach (ProductSearchModel prod in prods)
{
var xProduct =
new XElement("Product",
new XElement("ExternalId", prod.SKU),
new XElement("Name", prod.Description.Name),
new XElement("Description", prod.Description.Description),
new XElement("BrandExternalID", prod.Properties.Brand.FeedName),
new XElement("CategoryExternalId"), //look up the category here by sku
new XElement("ModelNumbers",
new XElement("ModelNumber", prod.SKU)),
new XElement("ManufacturingPartNumbers",
new XElement("ManufacturingPartNumber", prod.SKU)),
new XElement("UPCs",
new XElement("UPC", prod.UPC)),
new XElement("Attributes"),
new XElement("Attribute",
new XAttribute("id", "BV_FE_FAMILY"),
new XElement("Value"))
);
xProducts.Add(xProduct);
}

XML XElement building an xml document

Hi I am new to XML building, I basically didn't use it before, I always preferred json.
I have a solution where I just make it with string and convert to XML object, but how can I do it with XElement class?
This is the document:
<?xml version="1.0" encoding="utf-8"?>
<requestblock version="3.67">
<alias>ALIAS</alias>
<request type="AUTH">
<operation>
<sitereference>test12345</sitereference>
<accounttypedescription>TEST</accounttypedescription>
<parenttransactionreference>12-3-4567</parenttransactionreference>
</operation>
<merchant>
<orderreference>Example recurring auth</orderreference>
</merchant>
<customer> </customer>
<billing>
<amount currencycode="GBP">1234</amount>
<subscription type="TEST">
<number>1</number>
</subscription>
</billing>
<settlement/>
</request>
</requestblock>
I already have a part of the code like this:
XElement address =
new XElement("alias", "TEST",
new XElement("request", new XAttribute("type", "AUTH"),
new XElement("City", "Mercer Island"),
new XElement("State", "WA"),
new XElement("Postal", "68042")
));
But I have a problem with alias, because alias is closed after all elements, not in the same notation:
<alias>TEST
<request type="AUTH">
<City>Mercer Island</City>
<State>WA</State>
<Postal>68042</Postal>
</request>
</alias>
As you can see notation is the problem.
You're setting alias as your root element, which should be requestblock. If you start with requestblock like this:
XElement address =
new XElement("requestblock", new XAttribute("version",3.67),
new XElement("alias", "TEST"),
new XElement("request", new XAttribute("type", "AUTH"),
new XElement("City", "Mercer Island"),
new XElement("State", "WA"),
new XElement("Postal", "68042")
It'l give you
<requestblock version="3.67">
<alias>TEST</alias>
<request type="AUTH">
<City>Mercer Island</City>
<State>WA</State>
<Postal>68042</Postal>
</request>
</requestblock>

How do I do this in C#?

The following Unit Test passes in VB.Net
<Test()> _
Public Sub VB_XMLLiteral_SyntaxRocks_Test()
Dim XML = <Doc>
<Level1>
<Item id="1"/>
<Item id="2"/>
</Level1>
<Level1>
<Item id="3"/>
<Item id="4"/>
</Level1>
</Doc>
Assert.AreEqual(4, XML.<Level1>.<Item>.Count)
End Sub
How do I assert the same thing in C#?
To clarify, I 'd like to know how to express...
XML.<Level1>.<Item>
...in C#
Assert.AreEqual(4, XML.Elements("Level1").Elements("Item").Count());
And of course XML needs to be an XElement (that's what a VB literal produces too)
Using LINQ to XML:
var XML = new XElement("Doc",
new XElement("Level1",
new XElement("Item",
new XAttribute("Id", 1)),
new XElement("Item",
new XAttribute("Id", 2))),
new XElement("Level1",
new XElement("Item",
new XAttribute("Id", 3)),
new XElement("Item",
new XAttribute("Id", 4))));
Assert.AreEqual(4,
(from element in XML.Elements("Level1").Elements("Item")
select element).Count());

Reading XML Attribute with C#

I have an the incoming XML file like this.
<Sport id="23" name="Basketbol">
<Country id="15" name="A.B.D.">
<Tournament id="9" name="Amerikan Basketbol Ligi" type="1" typeDesc="League">
<Season id="95" name="2010/2011">
<Stage id="777" name="2010/2011 Sezonu">
<Team id="104" name="Chicago Bulls" conferenceId="3" conference="Merkez">
<General position="1" totalPlayed="82" won="62" draw="0" lost="20" goalsScored="8087" goalsAgainst="7485" points="144" form="W- W- W- W- W- W" />
<Home position="1" totalPlayed="41" won="36" draw="0" lost="5" goalsScored="4104" goalsAgainst="3683" points="77" />
<Away position="4" totalPlayed="41" won="26" draw="0" lost="15" goalsScored="3983" goalsAgainst="3802" points="67" />
</Team>
I want to save an XML File as below in c#.
<sport>
<id>23></id>
<name>Basketbol</name>
<Country>
<id>15</id>
<name>A.B.D.</name>
LinqToXml: http://msdn.microsoft.com/en-us/library/bb387098.aspx
using System.Xml.Linq;
// [...]
var sourceDoc = XDocument.Load(#"C:\Your\Source\Xml\File.xml");
var sports = sourceDoc.Descendants().Where(s => s.Name == "Sport");
var destDoc = new XDocument(sports.Select(s =>
new XElement("sport",
new XElement("id", s.Attribute("id").Value),
new XElement("name", s.Attribute("name").Value),
new XElement("country",
new XAttribute("id", s.Descendants().Where(c => c.Name == "Country").FirstOrDefault().Attribute("id").Value),
new XAttribute("name", s.Descendants().Where(c => c.Name == "Country").FirstOrDefault().Attribute("name").Value)
)
)
));
destDoc.Save(#"C:\Your\Destination\Xml\File.xml");

Insert new XML node using LINQ

XML:
<Questions>
<Question>
<Id>1</Id>
<Text>aaa</Text>
<Reserver />
</Question>
<Question>
<Id>2</Id>
<Text>bbb</Text>
<Reserver />
</Question>
</Questions>
How can insert new Question using LINQ like this:
<Question>
<Id>3</Id>
<Text>ccc</Text>
<Reserver />
</Question>
XDocument doc = XDocument.Parse("<Questions>...</Questions>");
doc.Root.Add(
new XElement("Question",
new XElement("Id", 3),
new XElement("Text", "ccc"),
new XElement("Reserver"))
);
You can create a new element like this:
var newElem = new XElement("Question",
new XElement("Id", 3),
...
);
xdoc.Root.Add(newElem);

Categories