Adding multiple child nodes to the Parent node - c#

I am trying to create and XML file with data I am collecting, however for a particular I need multiple sub tags to be generated
So I wish to have something like this:-
<Feedbacks>
<Feedback>
<Name></Name>
<Surname></Surname>
<Images>
<Image></Image>
<Image></Image>
<Image></Image>
</Images>
</Feedback>
</Feedbacks>
Sorry do not know how to paste the correct XML file here, but I think you get the idea. At the moment I have this code that is working:-
private static void CreateFeedbackXMLFile()
{
XmlDocument doc = new XmlDocument();
XmlNode docNode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
doc.AppendChild(docNode);
XmlNode Node = doc.CreateElement("Feedbacks");
doc.AppendChild(Node);
string fileName = "Feedback.xml";
string filePath = Properties.Settings.Default.DefaultFolder + "\\" + fileName;
doc.Save(filePath);
}
public static void InsertFeedback(Feedback feedback)
{
CreateFeedbackXMLFile();
string filePath = Properties.Settings.Default.DefaultFolder + "\\Feedback.xml" ;
XDocument xmlDoc = XDocument.Load(filePath);
XElement XParentElement = new XElement("Feedback");
InsertIntoXMLDoc(feedback, filePath, xmlDoc);
}
private static void InsertIntoXMLDoc(Feedback feedback, string filePath, XDocument xmlDoc)
{
xmlDoc.Element("Feedbacks").Add(new XElement("Feedback",
new XElement("Name", feedback.Name),
new XElement("Surname", feedback.Surname),
new XElement("Email", feedback.Email),
new XElement("Website", feedback.Website),
new XElement("Suggestion", feedback.Suggestion),
new XElement("Error", feedback.Error),
new XElement("MailingList", feedback.MailingList),
new XElement("Comments", feedback.Comments)
));
}
xmlDoc.Save(filePath);
}
Now I need to loop through the imageList and create nodes according to how many images I have.
Thanks for your help and time

Looping is the way to go for what you are trying. In fact, there is no "loopless" way to achieve that. You can, however, disguise the loop as a LINQ query, with something like this:
xmlDoc.Element("Feedbacks").Add(
/* All the elements before your image list */
XElement("images",
from img in myImageList select new XElement(...)
)
/* All the elements after your image list, preceeded by a comma */
);
Of course, you'll need to replace myImageList with your actual collection of images. Note that if you have an ImageList control, the actual collection is not the control itself, but its Images property.
Also, on the ..., you'll need to put whatever logics you are using to create each node from an image (using the auto-typed local variable img to refer to the appropriate image for each node).

Related

Remove root node and first child from XML document, while keeping second child node

I am currently developing a custom pipeline component for an XML document flow, where the root node and the first child of that root node needs to be stripped off, leaving only the second child node left (which is now the new root node).
I'm using XDocument as container class for the XML document. Ive written some code which gets the second child node, and creates a new XML document with that node as the root, thus removing the two undesired nodes from the picture.
XNode secondChild = xDoc.Root.Elements().First().NextNode;
XDocument outputXml = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
secondChild);
But when I test this setup in Biztalk, I only get an empty document back as a response. It seems to create an empty XML document which is then returned.
To give an example of what I want to achieve:
I want to go from a structure like this:
<Root>
<FirstChild></FirstChild>
<SecondChild></SecondChild>
</Root>
To a simple structure like this:
<SecondChild></SecondChild>
The full code of the Execute method in the pipeline:
public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
var originalStream = pInMsg.BodyPart.GetOriginalDataStream();
XDocument xDoc; //new XML document to return as the message
using (XmlReader reader = XmlReader.Create(originalStream))
{
reader.MoveToContent();
xDoc = XDocument.Load(reader);
}
XNode secondChild = xDoc.Root.Elements().First().NextNode;
XDocument outputXml = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
secondChild);
// Returning stream, serializing the XML to byte array
byte[] output = System.Text.Encoding.ASCII.GetBytes(outputXml.ToString());
MemoryStream memoryStream = new MemoryStream();
memoryStream.Write(output, 0, output.Length);
memoryStream.Position = 0;
pInMsg.BodyPart.Data = memoryStream; //overwrite the original message with the modified stream
return pInMsg;
}
Looking around on SO I found this answer, which I tried to follow, but as mentioned it produces an empty document. Is there a different option, other than simply creating a new XDocument?
If you develop a regular map to edit the document you can place it in the maps section of the receive port. It's simpler to create, test, and install than a pipeline component.

Force XmlDocument to save empty elements with an explicit closing tag

I have the following code:
string PathName = "C:\\Users\\TestUser\\Documents\\Project";
string FileName = "settings.xml";
XmlDocument Settings = new XmlDocument();
Settings.Load(Path.Combine(PathName, FileName));
XmlNode KnowledgeNode = Settings.SelectSingleNode("/Config/Map/Knowledge");
XmlNode UsersNode = Settings.CreateNode(XmlNodeType.Element, "Users", null);
XmlAttribute FileDirectory = Settings.CreateAttribute("FileDirectory");
FileDirectory.Value = UserSelectedFileDirectory;
UsersNode.Attributes.Append(FileDirectory);
KnowledgeNode.AppendChild(UsersNode);
Settings.Save(Path.Combine(PathName, FileName));
This results in my XML file containing <Users FileDirectory="C:\data" />
instead of <Users FileDirectory="C:\data" ></Users> as I want.
How do I create the end element? I've given it a Google and I can't find much. Any help is appreciated, thanks.
Here are three ways to force XmlDocument.Save to output a separate end tag for an empty XmlElement instead of an empty, self-closing tag.
Method 1
Insert an empty whitespace node inside the element:
UsersNode.AppendChild(Settings.CreateWhitespace(""));
Here's the output:
<Users FileDirectory="C:\data"></Users>
Method 2
Set the XmlElement.IsEmpty property of the element to false:
((XmlElement)UsersNode).IsEmpty = false;
Note that with this method, the default XML formatting settings will insert a line break between the start tag and the end tag. Here's the output:
<Users FileDirectory="C:\data">
</Users>
Method 3
Derive a custom XmlTextWriter that forwards all WriteEndElement calls to WriteFullEndElement:
public class CustomXmlTextWriter : XmlTextWriter
{
public CustomXmlTextWriter(string fileName)
: base(fileName, Encoding.UTF8)
{
this.Formatting = Formatting.Indented;
}
public override void WriteEndElement()
{
this.WriteFullEndElement();
}
}
Usage:
using (var writer = new CustomXmlTextWriter(Path.Combine(PathName, FileName)))
{
Settings.Save(writer);
}
This method might require less code overall if you have a lot of empty elements in your document.
As with Method 2, the default XML formatting settings will insert a line break between the start tag and end tag of each empty element.

Wrap XML root node with parent node

I have a .net Web Api 2 application that delivers data in XML.
My problem:
One of my classes looks like this:
public class Horse
{
public string Name { get;set; }
public string Category { get;set; }
}
When i serialize this, the result is:
<Horse>
<Name>Bobo</Name>
<Category>LargeAnimal</Category>
</Horse>
What i want is to wrap all outgoing XML content with a root element like this:
<Animal>
<Horse>
.....
</Horse>
</Animal>
I was hoping to do this in a custom XmlFormatter. But i can't seem to figure out how to append a root element on the writestream.
What is the best way to resolve this issue?
I have tried tweaking this answer to work in my custom xmlserializer, but doesn't seem to work. How to add a root node to an xml?
( I had a really short amount of time to write this question, so if anything is missing, please leave a comment.)
So.. Tweaked the answer to this question: How to add a root node to an xml? to work with my XmlFormatter.
The following code works, although i feel this is a hackish approach.
public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
{
return Task.Factory.StartNew(() =>
{
XmlSerializer xs = new XmlSerializer(type);
XmlDocument temp = new XmlDocument(); //create a temporary xml document
var navigator = temp.CreateNavigator(); //use its navigator
using (var w = navigator.AppendChild()) //to get an XMLWriter
xs.Serialize(w, value); //serialize your data to it
XmlDocument xdoc = new XmlDocument(); //init the main xml document
//add xml declaration to the top of the new xml document
xdoc.AppendChild(xdoc.CreateXmlDeclaration("1.0", "utf-8", null));
//create the root element
var animal = xdoc.CreateElement("Animal");
animal.InnerXml = temp.InnerXml; //copy the serialized content
xdoc.AppendChild(animal);
using (var xmlWriter = new XmlTextWriter(writeStream, encoding))
{
xdoc.WriteTo(xmlWriter);
}
});
}

Open XML adding images in multiple picture content controls

ok, old question is gone and this is new one:
#JasonPlutext, we decided to do it the way you suggested. custom xml looks like:
<DATA>
<BLOCK>
<FNAME>Test</FNAME>
<LNAME>Test1</LNAME>
</BLOCK>
<PICTURE>
<SIG> domain\username</SIG>
</PICTURE>
</DATA>
Text controls are binded: $rowBlock.FNAME, $rowBlock.LNAME and picture content control is $rowPicture.SIG.
text from xml is displayed, but there is no picture...
Picture is returned by ws (web service input parameter is domain\username from <sig> and picture is returned as byte[]).
//this is part of code where dealing with picture content control
picture[] pic = getPic("domain\username");
Paragraph tP = new Paragraph();
ParagraphProperties tParagraphProperties =
pControl.Descendants<ParagraphProperties>).FirstOrDefault();
tP.ParagraphProperties = (ParagraphProperties)tParagraphProperties.Clone();
...?...
Please suggest what to do next and how to bind picture?
thx
You could consider a slightly different approach.
You can bind a picture content control to an element in a custom xml part which contains a base64 encoded image.
If you do it this way, you can rely on Word to resolve the binding (ie update the image on the document surface with the one in the custom xml part). Or you can mimic what Word does yourself; docx4j.NET contains code to do that for you.
Doing it this way becomes a matter of just updating the custom xml part with the images you want.
Jason, i'm injecting base64 encoded image content as you said, but there is still no picture. in customXml folder of zip document, in item3.xml there is a base64 string inside tag, but in media folder there is only default image. don't know what's wrong... my procedure is:
//first, searching for drawing inside current processing control
`Drawing tDraw = pControl.Descendants<Drawing>().FirstOrDefault();
//if there is a drawing element, then clone control
OpenXmlElement tClone = (OpenXmlElement)pControl.Clone();
//then call method:
private static void insertPicture(OpenXmlElement pControl)
{
//WordprocessingDocument wordDoc = WordprocessingDocument.Open(dokument, true);
MainDocumentPart mainPart = dokument.MainDocumentPart;
CustomXmlPart customPart = mainPart.CustomXmlParts.FirstOrDefault();
//convert image into string
string picName = #"c:\temp\picasso.png";
System.IO.FileStream fileStream = System.IO.File.Open(picName, System.IO.FileMode.Open);
System.IO.BinaryReader br = new System.IO.BinaryReader(fileStream);
byte[] byteArea;
byteArea = br.ReadBytes(System.Convert.ToInt32(fileStream.Length));
string picString = System.Convert.ToBase64String(byteArea);
//Load the XML template
string DataString = iData["DATA"].ToString();
//Properties.Resources.XMLData;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(DataString);
//change the value
XmlNodeList xmlNode = xmlDoc.GetElementsByTagName("picture");
xmlNode[0].InnerText = picString;
//write the custom xml data into the customxmlpart
System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(customPart.GetStream(System.IO.FileMode.Create), System.Text.Encoding.UTF8);
writer.WriteRaw(xmlDoc.InnerXml);
writer.Flush();
writer.Close();
fileStream.Close();
br.Close();
mainPart.Document.Save();
//dokument.Close();
}
then append control to document
OpenXmlElement tC1 = pControl;
IEnumerable<Run> tEl1 = tClone.Descendants<Run>();
if (tEl1.Count() != 0)
{
foreach (OpenXmlElement tElement in tEl1.Reverse())
{
OpenXmlElement tClone1 = (OpenXmlElement)tElement.Clone();
tC1.InsertBeforeSelf(tClone1);
tC1 = tClone1;
}
}`

how to append a xml file in c#?

i am adding tracing for audit purposes of a simple process i have built as an .exe and set in the scheduler to run every 10 minutes. i want to have the application output the results into an xml file.
if the file exists then open and append data to it, if it does not exist i want to create a new xml file that will be persisted and used on next run.
here is my code now, what do i need to add, how do i open the xml file (on c:/file.xml) and use it to append nodes to?
static void Main(string[] args)
{
XmlDocument doc = new XmlDocument();
XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", null, null);
doc.AppendChild(dec);// Create the root element
XmlElement root = doc.CreateElement("STATS");
doc.AppendChild(root);
XmlElement urlNode = doc.CreateElement("keepalive");
urlNode.SetAttribute("runTime", DateTime.Now.ToString());
try
{
WebProxy wp = new WebProxy("http://proxy.ml.com:8083/");
WebClient w = new WebClient();
w.Proxy = wp;
if (w.DownloadString("http://wwww.example.com") != "")
urlNode.SetAttribute("result", "UP");
else
urlNode.SetAttribute("result", "DOWN");
}
catch
{
urlNode.SetAttribute("result", "DOWN");
}
finally
{
root.AppendChild(urlNode);
doc.Save("c:/keepAlive.xml");
}
}
You can't append an XML file - you'll have to load the file in memory , modify/add/etc, and then write it to disk.
EDIT :
Well, for loading a file you would use :
XmlDocument xmlDoc= new XmlDocument(); // create an xml document object.
if(System.IO.File.Exists("yourXMLFile.xml")
xmlDoc.Load("yourXMLFile.xml");// load from file
else{
// create the structure of your xml document
XmlElement root = xmlDoc.CreateElement("STATS");
xmlDoc.AppendChild(root);
}
and then start adding the keepalive stuff.
I would actually go a bit further and not mess around with xml. I'd create a class that contains everything I need and just serialize and deserialize it.
Like this:
[XmlRoot]
public class Stats{
public Stats(){}
public IList<StatsItem> Items{get;set;}
}
public class StatsItem{
public StatsItem(){}
public string UrlName{get;set;}
public DateTime Date{get;set;}
}
now just serialize this, and you have your xml document. When the time comes, deserialize it, add stuff to the Items list and serialize and save it to disk again.
There are lots of resources on google , so just search a bit for those.
using System;
using System.Xml.Linq;
using System.Xml.XPath;
...
public void Append(){
XDocument xmldoc = XDocument.Load(#"yourXMLFile.xml"));
XElement parentXElement = xmldoc.XPathSelectElement("yourRoot");
XElement newXElement = new XElement("test", "abc");
//append element
parentXElement.Add(newXElement);
xmldoc.Save(#"yourXMLFile.xml"));
}

Categories

Resources