Inserting a new element after an existing element in an SVG (XML) - c#

I want to insert an element after an existing element. For example, in this simple XML file, I want to insert an element after the "defs" element.
<?xml version="1.0" standalone="no"?>
<svg viewBox="0 0 790 790" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" stroke-linecap="round" stroke-linejoin="round" fill-rule="evenodd" xml:space="preserve" >
<defs >
<clipPath id="clipId0" >
<path d="M0,790 790,790 790,0 0,0 z" />
</clipPath>
</defs>
****I WANT TO INSERT MY NEW ELEMENT HERE
<g clip-path="url(#clipId0)" fill="none" stroke="rgb(0,0,0)" stroke-width="0.5" />
<g clip-path="url(#clipId0)" fill="rgb(0,255,0)" stroke="rgb(0,255,0)" stroke-width="0" >
<text transform="matrix(12.0023 0 -0 12.0023 130.206 546.583)" font-family="Arial,'sans-serif'" font-size="1.39636" >WORK REGION</text>
</g>
</svg>
Here is my attempted c# code:
public void AddBlackBackground(string xOrig, string yOrig, string xExt, string yExt )
{
XElement xE = new XElement("rect");
xE.Add(new XAttribute("x", xOrig));
xE.Add(new XAttribute("y", xOrig));
xE.Add(new XAttribute("width", xOrig));
xE.Add(new XAttribute("height", xOrig));
xE.Add(new XAttribute("style", "fill:black"));
XElement root = _XML_Doc.Root.Element("svg");
XElement defs = root.Element("defs");
defs.AddAfterSelf(xE);
}
The XElements root and defs are null when I run the code, so naturally the line defs.AddAfterSelf(xE); throws an Object reference not set error.
Any guidance is appreciated.

Try this with namespace
var ns = _XML_Doc.Root.Name.Namespace;
XElement root = _XML_Doc.Element(ns +"svg");
XElement defs = root.Element(ns + "defs");
defs.AddAfterSelf(xE);
Or to ignore namespace
_XML_Doc.Root
.DescendantsAndSelf()
.Elements()
.Single(d => d.Name.LocalName == "defs")
.AddAfterSelf(xE);

Related

Parsing XML file with LINQ getting wrong information

Im trying to parse an XML file from my program and I'm basing my code off this answer.
However the XML I'm using now is a bit more complex where I need to fill several nested lists with classes. Here are my two classes
public class Picture
{
private int mPicNumber;
private int mPicDuration;
private List<string> mToSay = new List<string>();
public Picture(int picNumber, int picDuration, List<string> toSay){...}
}
public class Sequence
{
string mName;
int mNumber;
List<Picture> mPictures = new List<Picture>();
public Sequence(string name, int number, List<Picture> pictures){...}
}
The XML looks like this
<sequences>
<sequence>
<name>Seq 2</name>
<number>1</number>
<picture>
<number>1</number>
<duration>5</duration>
<rows>
<text>text1</text>
<text>text2</text>
<text>text3</text>
</rows>
</picture>
<picture>
<number>2</number>
<duration>5</duration>
<rows>
<text>text1</text>
<text>text2</text>
<text>text3</text>
</rows>
</picture>
<picture>
<number>3</number>
<duration>5</duration>
<rows>
<text>text1</text>
<text>text2</text>
<text>text3</text>
</rows>
</picture>
</sequence>
<sequence>
<name>Seq 2</name>
<number>1</number>
<picture>
<number>1</number>
<duration>5</duration>
<rows>
<text>text1</text>
<text>text2</text>
<text>text3</text>
</rows>
</picture>
<picture>
<number>2</number>
<duration>5</duration>
<rows>
<text>text1</text>
<text>text2</text>
<text>text3</text>
</rows>
</picture>
<picture>
<number>3</number>
<duration>5</duration>
<rows>
<text>text1</text>
<text>text2</text>
<text>text3</text>
</rows>
</picture>
</sequence>
</sequences>
Here is the code for parsing the XML
XDocument xmlDoc = XDocument.Load("Sequences.xml");
List<Picture> pictures;
List<string> toSay;
mSequences = xmlDoc.Descendants("sequence").
Select(be => new Sequence(
(string)be.Element("name"),
(int)be.Element("number"),
pictures = xmlDoc.Descendants("picture").
Select(bf => new Picture(
(int)bf.Element("number"),
(int)bf.Element("duration"),
toSay = xmlDoc.Descendants("rows").
Select(bg =>
(String)bg.Element("text")).ToList())).ToList())).ToList();
After I run this I get a list with 2 Sequences (which is correct) and the name and number is correct. However Each sequence contain all 6 pictures from the XML file and those pictures doesn't contain anything from within the rows tag. I tried changing Descendants to Elements on the two inner lists but then I got 0 pictures in all sequences instead. I will admit I'm not very good at LINQ and this is very confusing to me.
The problem with your code is here pictures = xmlDoc.Descendants("picture"). & toSay = xmlDoc.Descendants("rows").. You are again querying the XML from the top rather you should be querying the already filtered data. You should use the instance variable be & bf respectively.
This will give you the expected output:-
var res = xdoc.Root.Elements("sequence")
.Select(be => new Sequence(
(string)be.Element("name"),
(int)be.Element("number"),
pictures = be.Elements("picture")
.Select(bf => new Picture(
(int)bf.Element("number"),
(int)bf.Element("duration"),
toSay = bf.Element("rows").Elements("text")
Select(bg =>
(String)bg).ToList()))
.ToList()))
.ToList();
Also, note how I have replaced Descendants with Elements. If you XML contains some inner node with same tag then you will get unexpected output.
pictures = xmlDoc.Descendants("picture")
It looks like you get the Pics from the whole document xmlDoc.Decendants,
but instead you need to get it for each be object I think. I can`t check it right now but i guess be.Decentans should be okay?

Get XML attribute Values by its Descendants

I have an XML in this Format and I want to get List of Line ID and its Name
<ArrayOfLines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LineStatus ID="0" StatusDetails="">
<BranchDisruptions />
<Line ID="1" Name="Line1" />
<Status ID="GS" CssClass="GoodService" Description="Good Service" IsActive="true">
<StatusType ID="1" Description="Line" />
</Status>
</LineStatus>
<LineStatus ID="1" StatusDetails="">
<BranchDisruptions />
<Line ID="2" Name="Line2" />
<Status ID="GS" CssClass="GoodService" Description="Good Service" IsActive="true">
<StatusType ID="1" Description="Line" />
</Status>
</LineStatus>
</ArrayOfLines>
and This is the code I have written:
String xmlFilePath = #"C:/myXML.xml";
XDocument xmlFile = XDocument.Load(xmlFilePath);
var query = from c in xmlFile.Descendants("LineStatus") select c;
but it is not returning me any results.
Here is my idea but you have to create list "namesList" and "idList" before. Try this:
XDocument xDoc = XDocument.Load("your xml file");
foreach (var elem in xDoc.Document.Descendants("line"))
{
idList.Add(elem.Attribute("ID").Value);
namesList.Add(elem.Attribute("Name").Value);
}
And you have full controll by index of each list to this data. After that you can also create object of these 2 elements
You have an xml namespace, you need to specify it with element names:
XNamespace ns = "http://www.w3.org/2001/XMLSchema-instance";
var query = from c in xmlFile.Descendants(ns + "LineStatus") select c;
Try this...
String xmlFilePath = #"C:/myXML.xml";
XDocument xmlFile = XDocument.Load(xmlFilePath);
var query = (from c in xmlFile.Descendants("Line")
select new {
ID=c.Attribute("ID").Value,
Name=c.Attribute("Name").Value
}).ToList();;

Copy a specific piece of XML to a string based on an Attribute

I have the following from a XIB file
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OGN-yD-hzb">
<rect key="frame" x="4" y="21" width="36" height="36"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<constraints>
<constraint firstAttribute="height" constant="36" id="Sgc-0Z-H82"/>
<constraint firstAttribute="width" constant="36" id="WLV-12-NHf"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<state key="normal" title="fdf">
<color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<state key="highlighted">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="abV-ij-rke">
<rect key="frame" x="276" y="21" width="36" height="36"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<constraints>
<constraint firstAttribute="width" constant="36" id="ps5-Pb-Ebc"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<state key="normal" title="Button">
<color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<state key="highlighted">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
</button>
I've already separated out the DestinationIds from the connections and the entire xib file has been loaded into an XDocument.
Is there a way that I can grab the specific button (with the children) with the corresponding ID? SelectSingleNode looks to be the way, but that returns null every time I pass in the attribute ID for the button.
At least there are 3 different approaches to get specific <button> by it's id attribute. You can use XDocument combined with either LINQ to XML standard methods or XPathSelectElement() extension like so :
var doc = XDocument.Parse(xml);
var xpathResult = doc.XPathSelectElement("//button[#id='abV-ij-rke']")
.ToString();
var queryResult = doc.Descendants("button")
.First(o => (string)o.Attribute("id") == "abV-ij-rke")
.ToString();
Or using older XML API, XmlDocument, as you mentioned about SelectSingleNode() :
var doc = new XmlDocument();
doc.LoadXml(xml);
var result = doc.DocumentElement
.SelectSingleNode("//button[#id='abV-ij-rke']")
.OuterXml;
All worked fine to get specific button element markup when tested against the XML you posted.
var buttons = doc.GetElementsByTagName("button");
foreach (var button in buttons )
{
Console.WriteLine(((XmlElement) button).GetAttribute("Id"));
Console.WriteLine(((XmlElement) button ).InnerText);
}
var rect= doc.GetElementsByTagName("rect ");
foreach (var button in buttons )
{
Console.WriteLine(((XmlElement) button).GetAttribute("ID"));
Console.WriteLine(((XmlElement) button).InnerText);
Console.WriteLine(((XmlElement)rect).ParentNode);
}
try it

How to Get Element Names from XElement Using C#?

I have a Xml and i have to get the element name
<Data>
<Test key="G" modifier="control" />
<Test1 key="E" modifier="control" />
<Test3 />
<Test4 />
</Data>
XDocument xd = XDocument.Load("..\\Cmd.xml");
IEnumerable<XElement> xeCmdData = Cmd.XPathSelectElements(".//Data");
foreach (XElement xeData in xeCmdData)
{
// here i am getting the whole xml how to get Element name ...
// Like <Data>
// <Test key="G" modifier="control" />
// <Test1 key="E" modifier="control" />
// <Test3 />
// <Test4 />
// </Data>
}
How to get Element names ?
XDocument xd = XDocument.Load("..\\Cmd.xml");
IEnumerable<string> names = xd.XPathSelectElements("//Data/*")
.Select(e => e.Name.LocalName);
Or without XPath
IEnumerable<string> names = xd.Descendants("Data")
.Elements()
.Select(e => e.Name.LocalName);
Result:
Test
Test1
Test3
Test4

Insert the data of richtextbox into existing xml in c# linq

I have a xml like this:
<?xml version="1.0" encoding="utf-8"?>
<assessment xmlns="http://xml.thinkcentral.com/pub/xml/hsp/assessment" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:tia="http://xml.thinkcentral.com/pub/xml/hsp/tia" xmlns:tibase="http://xml.thinkcentral.com/pub/xml/hsp/tibase" xsi:schemaLocation="http://xml.thinkcentral.com/pub/xml/hsp/assessment http://xml.thinkcentral.com/pub/xml1_2_6/hsp_assessment.xsd" isbn="9780547660455" buid="NA12_AG_G01CH01A" title="Chapter 1 Test Form A" num_questions="24" num_sections="1" type="Basal" intervenable="true" duration="P5Y" pausable="false" scramble="false">
<test_section id="1" name="Chapter 1 Test Form A" index="1">
<aaa testitem_id="NA12_AG_G01CH01A_01" template="hsp_testitem_mc1.xslt" id="1" bankable="true">
<tia:multipleChoiceTestItem total-points="1" questionType="Multiple Choice" sample="false" version_label="1.0">
<tia:directions>
<tia:tiDirectionLine>
<tia:textBody></tia:textBody>
</tia:tiDirectionLine>
<tia:address>Richtextbox Data</tia:address>
</tia:directions>
</tia:multipleChoiceTestItem>
</aaa>
<aaa testitem_id="NA12_AG_G01CH01A_02" template="hsp_testitem_mc1.xslt" id="2" bankable="true">
<tia:multipleChoiceTestItem total-points="1" questionType="Multiple Choice" sample="false" version_label="1.0">
<tia:directions>
<tia:tiDirectionLine>
<tia:textBody></tia:textBody>
</tia:tiDirectionLine>
<tia:address>Richtextbox Data</tia:address>
</tia:directions>
</tia:multipleChoiceTestItem>
</aaa>
</test_section>
</assessment>
I have to insert the the data according to the id of the aaa element.
<aaa testitem_id="NA12_AG_G01CH01A_01" template="hsp_testitem_mc1.xslt" id="1" bankable="true">
<aaa testitem_id="NA12_AG_G01CH01A_02" template="hsp_testitem_mc1.xslt" id="2"bankable="true">
if id="1" then data of ritchtextbox will be insert into tia:address node.
i am using the following code.
private void button2_Click(object sender, EventArgs e)
{
XDocument doc = XDocument.Load(#"d:\file.xml");
XNamespace ns = XNamespace.Get("http://tia.com");
var result= (from ele in doc.Descendants("aaa")
where ((string)ele.Attribute("id")) == "1"
select ele.Element(ns+"address")).FirstOrDefault();
if (result != null)
{
result.Value = richTextBox1.Text;
doc.Save(#"d:\file.xml");
}
MessageBox.Show("done");
}
its not working. how i do that?
First of al, the XML markup you have posted is not valid. I think the easiest way to read/write an XML document is Linq-XML. You have to import System.Xml.Linq namespace to use XDocument class and its method. Take a look at MSDN article.
XDocument doc = XDocument.Load(#"c:\file.xml");
var result = (from ele in doc.Descendants("aaa")
where ((string)ele.Attribute("id")) == "1"
select ele.Element("address")).FirstOrDefault();
if (result != null)
{
result.Value = richTextBox1.Text;
doc.Save(#"c:\file.xml");
}
XML document should be:
<?xml version="1.0" encoding="utf-8"?>
<root>
<aaa id="1">
<address>Hello World</address>
</aaa>
<aaa id="2">
<address>
write text of ritchtextbox here</address>
</aaa>
</root>
EDIT:
In OP, XML markup has some issues and I've fixes the markup (added namespace).
<?xml version="1.0" encoding="utf-8"?>
<aaa testitem_id="chapter1" template="hsp_testitem_mc1.xslt" id="1" bankable="true" xmlns:tia="http://tia.com">
<tia:multipleChoiceTestItem total-points="1" questionType="Multiple Choice" sample="false" version_label="1.0">
<tia:directions>
<tia:tiDirectionLine>
<tia:textBody />
</tia:tiDirectionLine>
<tia:address>I have to edited here.(Richtextbox data)</tia:address>
</tia:directions>
</tia:multipleChoiceTestItem>
</aaa>
Code to find <tia:address> and replace its value.
XDocument doc = XDocument.Load(file);
XNamespace ns = XNamespace.Get("http://tia.com");
var result = (from ele in doc.Descendants(ns + "address")
select ele).SingleOrDefault();
if (result != null)
{
result.Value = richTextBox1.Text;
doc.Save(file);
}
EDIT : After changes made by OP in opening post.
XDocument doc = XDocument.Load(file);
//Change the namespace
XNamespace ns = XNamespace.Get("http://xml.thinkcentral.com/pub/xml/hsp/tia");
var result = (
from ele in doc.Descendants(ns + "multipleChoiceTestItem")
where ele.Parent.Attribute("id").Value == "1"
select
ele.Descendants(ns+"address").FirstOrDefault()
).FirstOrDefault();
if (result != null)
{
result.Value = "World";
doc.Save(file);
}

Categories

Resources