Using XML to Linq to Iterate an ICollection - c#

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);
}

Related

Using LINQ to XML to create a pair of XElement on the same level for each Object in a List of Object

I'm trying to build an XML document that should output, for each object Journey in the list List<Journey> journeys :
<Journeys>
<id>1</id>
<length>28</length>
<id>2</id>
<length>44</length>
etc.
</Journeys>
I've tried to use LINQ to XML with the following code :
var journeyElement = new XElement("Journeys",
journeys.Select(j =>
new XElement("id", j.id),
new XElement("length", j.length));
Which is apparently not the expected syntax.
var journeyElement = new XElement("Journey",
journeys.Select(j => new
{
id = new XElement("id", j.id),
length = new XElement("length", j.length)
}));
Which doesn't produce the expected result.
How would one proceed to create a pair of XElement on the same level from a List using LINQ to XML ?
You can project each Journey into an array of two elements then flatten them into a single list with SelectMany:
var journeyElement = new XElement("Journeys",
journeys.SelectMany(j => new[] { new XElement("id", j.id), new XElement("length", j.length) }));
This produces the desired XML:
<Journeys>
<id>1</id>
<length>28</length>
<id>2</id>
<length>44</length>
</Journeys>
If you can change the XML structure like this,
<Journeys>
<Journey>
<id>1</id>
<length>28</length>
</Journey>
<Journey>
<id>2</id>
<length>44</length>
</Journey>
etc.
</Journeys>
This code will work.
List<Journey> journeys;
var journeyElement = new XElement("Journeys",
journeys.Select(j =>
new XElement("Journey",
new XElement("id", j.id),
new XElement("length", j.length)
)
)
);

LINQ to XML, with conditions and Variable XML Structure with C#

i have an big issue, and just lost like 5 hours on it.
I have to create an XML file, which will be populated by a database reg.
i have variable structure, which in some cases will use specific sub structure.
the logic is simple.
I have to get all agencys,
then in every agency i have to iterate a foreach cycle,
and get all houses that they whant to sell/rent.
But its not only houses, thereĀ“s apartaments, garages, and so on.
Each of this scenes have their own structure with different data.
This is not all, there must have an condition. Only writes xml childs only if they are supposed to.
piece of xml example
<Clients>
<Client>
<aggregator/>
<code/>
<reference/>
<contact>
<secondhandListing>
<property>
<code/>
<reference/>
<scope/>
<address>
<features/>
<operation> // variable structure
1example <price/>
<communityCosts/>
2example <price/>
<communityCosts/>
<depositType/>
</operation>
</property>
</secondhandListing>
Can some one show me an example of how this could be done.
what I archive until now was:
var agenciasConectores = //query of Agencys
foreach (var agenciaConector in agenciasConectores)
{
var imoveisAgencia = // query of homes in each Agency
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("Clients",
from agencia in agenciasConectores
select new XElement("Client", new XAttribute("ID", agencia.AgencyId),
new XElement("aggregator", agencia.ConnectorLicense),
new XElement("code", agencia.Name),
new XElement("Reference", agencia.Phone),
new XElement("contact", agencia.Phone),
new XElement("secondhandListing"),
new XElement("newbuildListing")
)));
foreach (var imovel in imoveisAgencia)
{
if (imoveisAgencia.Count() > 1)
{
doc.Document.Add(new XElement("property",
new XElement("code", "codigo"),
new XElement("reference", "reference"),
new XElement("scope", "scope"),
new XElement("address", "address"),
new XElement("contact", "contact"),
new XElement("features", "features"),
new XElement("operation", "operation"),
new XElement("description", "description")));
}
}
}
When I try to run this in Visual Studio, the following line throws an exception
doc.Document.Add(new XElement("property",
...
are you trying to do something more like this?
doc.Root.Add(new XElement("property",
...
or, perhaps, something closer to this
doc.Descendants("Client")
.Single(c => c.code == "something")
.Add(new XElement("property",
...
Already find a solution.
its simple.
XElement root = new XElement("root");
XElement child = new XElement("Child");
XElement child2 = new XElement(Child2);
then I populate the elements.
Child.SetValue(a);
child2.Setvalue(b);
and at final, i just add elements to parent Element.
root.add(Child,Child2);
But like that i also can make some validations(in my case in cycle)
If(a>b)
root.add(child);
else
root.add(child2);
thx for the try. I learn it by the example hehehhe

generating xml string and passing in namespace

i am having trouble generating an xml string when i add a space name. This is how i want to generate my xml like
xml sample:
<Feedback xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Record>
<ID>2FAC636E-F96C-4465-9272-760BAF73C0DF</QRCodeID>
<SubID>10B5236C-47FD-468D-B88D-D789CA0C663A</SubmissionID>
<UserID>1</UserID>
<Page>1</Page>
</Record>
<Record>
<ID>219C462B-B874-4408-AFBA-CA727922D50F</QRCodeID>
<SubID>10B5236C-47FD-468D-B88D-D789CA0C663A</SubmissionID>
<UserID>1</UserID>
<Page>2</Page>
</Record>
</Feedback>
What my code looks like now:
XDocument xdoc = new XDocument(
new XElement("Feedback xmlns:i='http://www.w3.org/2001/XMLSchema-instance'",
new XElement("Record",
new XElement("ID", idGuid),
new XElement("SubID", subGuid),
new XElement("UserID", 2),
new XElement("Page", pages)
)
)
);
when i run it throws an error here "Feedback xmlns:i='http://www.w3.org/2001/XMLSchema-instance'" that it doesn't like the character ' '
The documentation here suggests that something like the following should work:
XDocument xdoc = new XDocument(
new XElement("Feedback",
new XAttribute(XNamespace.Xmlns + "i", "http://www.w3.org/2001/XMLSchema-instance"),
new XAttribute(XNamespace.Xmlns + "j", "http://schemas.sitename.com/2013/03/Project.Models"),
new XElement("Record",
new XElement("ID", idGuid),
new XElement("SubID", subGuid),
new XElement("UserID", 2),
new XElement("Page", pages)
)
)
);
Have a look at this MSDN page, it explains everything you need to know
I'm pretty sure you need to use Namespaces.
This is trying to create a markup with your entire string, which is invalid.

Assigning ID to Objects transmitted in XML document

I have an array of objects and I want to pass them into a XML file. The Ojects lack of an attribute ID, in fact the form of the array is:
var people = new[]{
new {Name="James", Age="22", Company="FF"},
new {Name="Susan", Age="31", Company="PK"},
new {Name="Peter", Age="24", Company="TF"},
}
Is there any way to pass them in an xml file, granting to each one of them an ID starting from 1 and increased by 1 for each Object?
The desired form of the xml elemnts should look like:
<People>
<Person ID="1">
<Name>James</Name>
<Age>22</Age>
<Company>FF</Company>
....
</People>
It would be ideal if the solution would constist of one only LINQ query.
var result = new XDocument(
new XElement("People",
people.Select((p, i) =>
new XElement("Person",
new XAttribute("ID", i + 1),
new XElement("Name", p.Name),
new XElement("Age", p.Age),
new XElement("Company", p.Company)
)
)
)
);

Insert Element within specific tags

I'm trying to insert an element at a specific point within my file, then save that file out. However, I can't seem to get it right. My XML layout is like this:
<?xml version="1.0" encoding="utf-8"?>
<Settings>
<Items />
<Users />
</Settings>
This is my current code:
XDocument xd = XDocument.Load(#"C:\test.xml");
var newPosition = xdoc.Root.Elements("Users");
//I've tried messing around with newPosition methods
XElement newItem = new XElement("User",
new XAttribute("Name", "Test Name"),
new XAttribute("Age", "34"),
);
//how can I insert 'newItem' into the "Users" element tag in the XML file?
xd.Save(new StreamWriter(#"C:\test.xml"));
I'd like to use Linq to XML to insert 'newItem' into the tag. Thanks for any help on this.
Just find the Users element, and append it:
// Note that it's singular - you only want to find one
XElement newPosition = xdoc.Root.Element("User");
XElement newItem = new XElement("User",
new XAttribute("Name", "Test Name"),
new XAttribute("Age", "34"));
// Add the new item to the collection of children
newPosition.Add(newItem);
Your use of var here was leading you astray - because the type of newPosition in your code was really IEnumerable<XElement>... you were finding all the User elements. (Okay, there would only have actually been one element, but that's irrelevant... it was still conceptually a sequence.)

Categories