How to append new Child node to existing file using Rapidxml in c++ - append

Below is my code to create an xml using Rapidxml.
...
std::ofstream theFile(pathToCheck);
xml_document<> doc;
xml_node<>* root = doc.allocate_node(node_element, "ApplicationsDetails");
doc.append_node(root);
//child Node : <ApplicationData>
xml_node<>* child = doc.allocate_node(rapidxml::node_element, "ApplicationInfo");
child->append_attribute(doc.allocate_attribute("ApplicationID", "1234"));
child->append_attribute(doc.allocate_attribute("ApplicationTitle", "Saha"));
root->append_node(child);
theFile << doc;
theFile.close();
doc.clear();
...
And below are the nodes saved to file.
...
<ApplicationsDetails>
<ApplicationInfo ApplicationID="1234" ApplicationTitle="Saha" />
</ApplicationsDetails>
...
Now, I want to append new child node to that xml, like I have to open that file and read the nodes and append new child node like below, so total output should be
...
<ApplicationsDetails>
<ApplicationInfo ApplicationID="1234" ApplicationTitle="Saha" />
<ApplicationInfo ApplicationID="5678" ApplicationTitle="nagg" />
</ApplicationsDetails>
...
I tried this, but somehow , nodes are not appended properly, can any one suggest the code?

Related

Which property does the visio.Masters.MatchByName method refer to?

The Master of vsoDoc can integrate Masters with the same name at drop time by using the MatchByName method.
However, it may not work well in some cases, and we are investigating the cause.
Which property does MatchByName refer to to integrate Master? Is it Master.Name? Or is it Master.NameU?
If you divide the case
The copy destination is Master (vsoDoc.Masters) of the visio file (A.vsdx).
There can be two cases for the copy source.
One is to copy the Shapes of the visio file (B.vsdx)
Or drop it from the vssx file (X.vssx) to A.vsdx.
When copying Shapes, do you refer to the Name or NameU of each Master?
Since the ReplaceShame method changes the name of Master,
I am dealing with the new Master and the old Master by swapping the names with the following procedure.
First, in preparation for ReplaceShape, change the MatchByName of all vsoMasters to False.
Then "Replace Shape" all vsoShape
For Each vsoMaster in vsoMasters
vsoMaster.MatchByName = False
Next
Remove the replaced Master from Masters
For Each vsoPage In vsoDoc.Pages
For Each vsoShape in vsoPage.Shapes
If Not (vsoShape.Master is Nothing) then
vsoShape.Master.MatchByName = False
Set vsoShape = vsoShape.ReplaceShape(vssxMasters.ItemU(vsoShape.Master.NameU), 1)
end if
Next
Next
Remove the replaced Master from Masters
For Each vsoMaster in vsoMasters
If InStr(vsoMaster.Name, ".") == 0 then
vsoMasters.ItemU(vsoMaster.NameU).Delete
end if
Next
Change MatchByName of all vsoMasters to True
For Each vsoMaster In vsoMasters
vsoMaster.MatchByName = True
Next
For Each vsoPage In vsoDoc.Pages
For Each vsoShape in vsoPage.Shapes
vsoShape.Master.MatchByName = True
Next
Next
vsoDoc.SaveAs NewName
############################################################
I examined the phenomenon related to MatchByName in three cases.
MatchByName = True in all of the following cases.
Case A-1
When using Master (NameU: "test") of the same stencil file (M.vssx)
Procedure
Place the "test" stencil in the A.vsdx file.
Copy the A.vsdx file and create the A-1_copy.vsdx file
Copy the "test" stencil in which A_copy.vsdx, and pasting it into the A.vsdx file.
Result
As expected, MatchByName works fine and the document stencil Masters doesn't increase.
Case B
Create a Master (NameU: "test") in the stencil file (M-1.vssx)
Create another Master (NameU: "test") in another stencil file (M-2.vssx)
In this case B, the Master Name U is same, but the Base ID and Unique ID are different. I think this is an important point.
Case B-1
Procedure
Place the "test" stencil of the M-1.vssx in the B.vsdx file.
Place the "test" stencil of the M-2.vssx in the C.vsdx file.
Copy the "test" stencil from the C.vsdx file and paste it into the B.vsdx file.
MatchByName doesn't work. "test.10" is added to the document stencil.
I suspect that this is because the stencil's BaseID and UniqueID are different.
Case B-2
Procedure
Place the M-1.vssx "test" stencil in the B.vsdx file.
Launch another VISIO application and drop the "test" stencil of M-2.vssx into the B.vsdx file.
Result
Unlike the B-1 result, MatchByName WORKS.
 I can not understand the cause.
The following code is the code to rewrite the XML file in the vsdx file. By rewriting the Unique ID of the Masters of the vsdx file and the vssx file, MatchByName can be operated as expected.
import zipfile
from pathlib import Path
import xml.etree.ElementTree as ET
import sys
import shutil
import os
# https://yuukou-exp.plus/handle-xlsx-with-python-intro/
# https://livingdead0812.hatenablog.com/entry/2019/07/18/183322
# https://pg-chain.com/python-xml-read-write
# https://note.com/you_memolog/n/ne7d9e8d8a0d3
# https://stackoverflow.com/questions/16721010/write-a-xssfworkbook-to-a-zip-file
#visiopath = Path(sys.argv[1])
visiopath = Path(r"Flowchart_v1.0.17.vssx")
folderpath = visiopath.parent
xml_path = None
with zipfile.ZipFile(visiopath) as zf:
for info in zf.infolist():
if "masters.xml" in info.filename:
xml_path = info.filename
archive = zipfile.ZipFile(visiopath)
#archive = zipfile.ZipFile(visiopath, "w")
print(f"xml_path {xml_path}")
NameU_UniqueID_dict = {'OPERATOR' : '{00FBF49C-0014-0000-8E40-00608CF305B2}',
'TERMINATOR' : '{00634DFF-0015-0000-8E40-00608CF305B2}',
'BRANCH' : '{07639B38-0034-0000-8E40-00608CF305B2}',
'SQUARE' : '{07622477-0012-0000-8E40-00608CF305B2}',
'STARTER' : '{00489A6D-0032-0000-8E40-00608CF305B2}',
'CONNECTOR' : '{006BE21A-0004-0000-8E40-00608CF305B2}',
}
if xml_path is not None:
tree = ET.parse(archive.open(xml_path))
root = tree.getroot()
for child in root:
if child.attrib['NameU'] in NameU_UniqueID_dict.keys():
UniqueID = NameU_UniqueID_dict[child.attrib['NameU']]
child.attrib['UniqueID'] = UniqueID
tree.write('masters.xml')
print('finished writing.')
with zipfile.ZipFile(visiopath, 'a') as zf:
zf.write('masters.xml', arcname='visio\masters\masters.xml')
del archive
zippath = visiopath.with_suffix('.zip')
print(zippath)
print(visiopath)
shutil.move(visiopath, zippath)
shutil.unpack_archive(zippath, 'dir_out')
os.remove(zippath)
shutil.make_archive(zippath.parent / zippath.stem, format='zip', root_dir='dir_out')
#shutil.make_archive(zippath, format='zip', root_dir='dir_out')
shutil.move(zippath, visiopath)
When copying Shapes, do you refer to the Name or NameU of each Master?
NameU
When working with Visio objects, developers should always use universal names to reference their objects. This advice applies to more than names. A number of properties and methods in the Visio API have universal syntax equivalents. If you find a property and notice the same property exists but ending in a ‘U’, use the universal property. A classic example is to use the FormulaU property to get and set formulas instead of Formula.
Read more about Name and NameU
when you use ReplaceShape method in Document stecnil created new master, this new master have different Name and NameU anyway !

Word cannot open DOCX file with a table

I am trying to run mail merge against a DOCX file using Open XML API - just replacing a <w:t> element with a table (see below). Even the simplest table created using the following code results in Word erroring out when opening the file.
If I get rid of the row (so that I only have <w:tbl> / <w:tblGrid> / <w:GridCol>). there is no error, but then I cannot have any data of course.
Can anybody see what I am doing wrong?
Table table = new Table(new TableGrid(new GridColumn() { Width = "2000"}),
new TableRow(new TableCell(new Paragraph(new Run(new Text("test")))))
);
TextNode.Parent.ReplaceChild<Text>(table, TextNode);
You cannot replace <w:t> with <w:tbl>. The table is a block-level element so you can place it in the same places where you have the paragraph (<w:p>).
In other words, you can place it as a child element of one of the following: body, comment, customXml, docPartBody, endnote, footnote, ftr, hdr, sdtContent, tc, and txbxContent.
So, try something like this:
// TextNode (Text) -> Parent (Run) -> Parent (Paragraph)
var paragraph = TextNode.Parent.Parent as Paragraph;
paragraph.Parent.ReplaceChild(table, paragraph);
EDIT:
If the parent element is <w:tc>, you should add an empty paragraph to its end:
// TextNode (Text) -> Parent (Run) -> Parent (Paragraph)
var paragraph = TextNode.Parent.Parent as Paragraph;
var parent = paragraph.Parent;
parent.ReplaceChild(table, paragraph);
if (parent is TableCell)
parent.InsertAfter(new Paragraph(), table);

The process cannot access the file because it is being used by another process --- EF Core serializing XML

I am trying to write a method to fetch the categories and their respective products from the Northwind database and to then use xml serialization to write to a file.
I have tried the following code but get the error detailed in the heading. (The file is created but no XML is written to it).
Is anyone able to advise what is wrong with my code ? Any assistance would be greatly appreciated. Thank you.
static async void SerializeCategoriesWithXML() {
FileStream xmlFileStream = null;
XmlWriter xml = null;
// Create file to write to :
string path = Combine(CurrentDirectory, "CategoriesAndTheirProducts.xml");
// Create a file stream :
xmlFileStream = File.Create(path);
// Wrap the file stream in an Xml writer helper and automatically indent the nested elements :
xml = XmlWriter.Create(xmlFileStream, new XmlWriterSettings { Indent = true });
using (var db = new NorthwindContext())
{
// A query to get all categories and their related products :
IQueryable<Categories> cats = db.Categories
.Include(c => c.Products
.ToList());
await using (FileStream stream = File.Create(path))
{
// Write the Xml declaration :
xml.WriteStartDocument();
// Serialize the object graph to the stream :
foreach (Categories c in cats)
{
// Write a root element :
xml.WriteStartElement("category");
foreach(Products p in c.Products)
{
xml.WriteElementString("product", p.ProductName);
}
// Write the closing root element :
xml.WriteEndElement();
xml.Flush();
}
// CLose the helper and stream :
xml.Close();
xmlFileStream.Close();
}
}
}
There are multiple problems with the code being shared.
Lets try to understand each problem and think of a possible solution to the problem -
The problem statement as far as I understand is, you wanted to create a XML file with category and products under the category. So for simplicity I assume you are trying to get a XML file as below -
<?xml version="1.0" encoding="utf-8"?>
<categories>
<category>
<product>Chai</product>
<product>Chang</product>
<product>Guaraná Fantástica</product>
<product>Sasquatch Ale</product>
<product>Steeleye Stout</product>
<product>Côte de Blaye</product>
</category>
<category>
<product>Aniseed Syrup</product>
<product>Chef Anton's Cajun Seasoning</product>
<product>Chef Anton's Gumbo Mix</product>
<product>Grandma's Boysenberry Spread</product>
</category>
</categories>
Coming to what's wrong with above posted code -
Problem 1: Multiple Creation of file with the specified path -
// Create a file stream :
xmlFileStream = File.Create(path);
you have already fired File.Create in the above line so when you are firing the below code it is saying file already in use.... (the below line is not required)
await using (FileStream stream = File.Create(path))
Problem 2: Linq query is not right. You can replace your linq query with the below code -
var cats = db.Categories
.Include(c => c.Products).ToList();
Problem 3: Xml construction is wrong...
You need to wrap the category tag inside a parent as multiple category objects will get created. Also in the code above you are trying to flush the xml when one category is read. You need to perform flush once the last
xml.WriteEndElement();
is executed.
So you can replace the code block for creating xml as below -
// Write the Xml declaration :
xml.WriteStartDocument();
xml.WriteStartElement("categories");
// Serialize the object graph to the stream :
foreach (Categories c in cats)
{
// Write a root element :
xml.WriteStartElement("category");
foreach (Products p in c.Products)
{
xml.WriteElementString("product", p.ProductName);
}
// Write the closing root element :
xml.WriteEndElement();
}
xml.WriteEndElement();
xml.Flush();
// CLose the helper and stream :
xml.Close();
xmlFileStream.Close();
Now the file should get created with the categories->category[].
And each category->product[].
Thanks

Create an Array from list of objects in MaxScript and add them to a new layer

I'm super new to Maxscript and want to automate a process, I've been looking at some tutorials, but I'm running into a issue with selection. What I'm trying to do, is I have a list of strings (that I might have to add to) that represent objects in the max file that I want to select (if they exist in that file) and then add to a new layer.
for instance:
/* I have a big long list of objects I want to mass select, this has to be hardcoded because its a similar list that exists in a ton of max files */
rObj1 = "testObj1"
rObj2 = "sampleObj2"
""
rObj99 = "newObj90"
/*I want to then add it to an array
removeList = #(rObj***)
/* Then run through each entry in the array to make sure it exists and then add it to my selection
for i in removeList do
(
if i != undefined then select (i)
)
/*Then Add what I have selected to a new layer
newLayer = LayerManager.newLayerFromName "removed_list"
for obj in selection do newLayer.addNode obj
I keep getting an error when it comes to selection, being new to Max I'm not sure what to do.
You are trying to select string where you should be selecting (or adding to the layer) an object:
newLayer = LayerManager.newLayerFromName "removed_list"
for objName in removeList where isValidNode (getNodeByName objName) do
newLayer.addNode (getNodeByName objName)

OpenXml: Copy OpenXmlElement between documents

I have two Word documents (WordprocessingDocument), and I want to replace the contents of an element in the first with the contents in the body of the second one.
This is what I'm doing right now:
var docA = WordprocessingDocument.Open(docAPath, true);
var docB = WordprocessingDocument.Open(docBPath, true);
var containerElement = docA.MainDocumentPart.Document.Body
.Descendants<SdtBlock>()
.FirstOrDefault(sdt => sdt.SdtProperties.Descendants<SdtAlias>().Any(alias => alias.Val == containerElementName))
.SdtContentBlock;
var elementsToCopy = docB.MainDocument.Part.Document.Body.ChildElements.Where(e => e.LocalName != "sectPr"));
containerElement.RemoveAllChildren();
containerElement.Append(elementsToCopy);
Basically I get the container (an SdtBlock) from the first document using its alias to identify it, then get all the children of the second element (removing the SectionProperties which I don't want to copy) and then try to add those to the container element.
The problem is that I'm getting this exception:
Cannot insert the OpenXmlElement "newChild" because it is part of a tree.
When I invoke the last line on that code (the Append).
Any ideas on how can I achieve what I want?
You need to clone the element to copy containerElement.Append(elementsToCopy.CloneNode(true));
The elementsToCopy is still attached to it's original tree. So you would have to remove it's parents or copy them( to keep the original intact). I think there exists a removeParent() method.