0

I have the following code:

  XNamespace testNM = "urn:lst-emp:emp";
                XDocument xDoc;
                string path = "project_data.xml";
                if (!File.Exists(path))
                {
                    xDoc = new XDocument(
                               new XDeclaration("1.0", "UTF-16", null),
                               new XElement(testNM + "Test")
                               );
                }
                else
                {
                    xDoc = XDocument.Load(path);
                }

                var element = new XElement("key",
                                        new XElement("Type", type),
                                        new XElement("Value", value));
                xDoc.Element(testNM + "Test").Add(element);

                // Save to Disk
                xDoc.Save(path);

which produces an output in the XML file like this:

<?xml version="1.0" encoding="utf-16"?>
<Test xmlns="urn:lst-emp:emp">
  <key xmlns="">
    <Type>String</Type>
    <Value>somestring</Value>
  </key>
</Test>

How can I get an output like this in the XML file instead?

<?xml version="1.0" encoding="utf-16"?>
<Tests xmlns="urn:lst-emp:emp">
  <key name="someString">
    <Type>String</Type>
    <Value>somestring</Value>
  </key >
</Tests>

Note its only the 3rd line that has changed in the XML file.

john
  • 1,561
  • 3
  • 20
  • 44
  • 1
    Well, you need a `name` attribute, and you presumably need your element to be in the default namespace in your context - which we can't tell because you've only shown that element rather than the wider document of which it's a part. Those are two different issues - what have you tried for each of them? – Jon Skeet Aug 21 '14 at 08:10
  • My code has been edited. Check it above! – john Aug 21 '14 at 08:15

3 Answers3

3

You can do it this way:

var element = new XElement("key",
                           new XAttribute("name", "someString"),
                           new XElement("Type", "type"),
                           new XElement("Value", "value"));
Georgi Bilyukov
  • 645
  • 4
  • 11
  • Don't forget about the mismatched namespace. The fact that the OP's code outputted an explicit `xmlns=""` that wasn't desired in the output means that he needs to specify the namespace to be whatever its parent is. So yes, but that should be noted. He'll (probably) want to say `...new XElement(XName.Get("key", someString), ...`. – Matthew Haugen Aug 21 '14 at 08:14
  • I updated the question with the entire code. Pls take a look! – john Aug 21 '14 at 08:15
  • You can remove all the namespaces using this approach: http://stackoverflow.com/questions/987135/how-to-remove-all-namespaces-from-xml-with-c – Georgi Bilyukov Aug 21 '14 at 08:20
  • Thanks! Your code works perfectly. Also, how can I access the attribute 'name'? `currentElement.Name.LocalName` is what I used previously. – john Aug 21 '14 at 08:26
2

To provide a complete version of Bilyukov's answer, this should produce the output expected. Obviously substitute the "someString" static string with a variable populated as you wish. Changes include using XName.Get(string, string) to create the appropriate XName objects for the XElement constructors.

 XNamespace testNM = "urn:lst-emp:emp";
            XDocument xDoc;
            string path = "project_data.xml";
            if (!File.Exists(path))
            {
                xDoc = new XDocument(
                           new XDeclaration("1.0", "UTF-16", null),
                           new XElement(XName.Get("Tests", testNM.NamespaceName))
                           );
            }
            else
            {
                xDoc = XDocument.Load(path);
            }

            var element = new XElement(XName.Get("key", testNM.NamespaceName),
                                    new XAttribute("name", "someString"),
                                    new XElement("Type", type),
                                    new XElement("Value", value));
            xDoc.Element(XName.Get("Tests", testNM.NamespaceName)).Add(element);

            // Save to Disk
            xDoc.Save(path);
john
  • 1,561
  • 3
  • 20
  • 44
toadflakz
  • 7,764
  • 1
  • 27
  • 40
  • Thanks, this is great! If I wanted to access an element based on the 'name' attribute (eg: element with key name=abc), how would I do that? – john Aug 21 '14 at 08:32
  • Also, your code has some errors. `XName.Get()` has some invalid arguments. – john Aug 21 '14 at 08:38
  • Probably best to ask that as a separate question but offhand - `xDoc.Element(XName.Get("Tests", testNM)).Elements(XName.Get("key", testNM)).Where(x => x.Attribute("name") != null && x.Attribute("name").Value == "abc").Single()` – toadflakz Aug 21 '14 at 08:39
  • Thanks, I'll work on that. `XName.Get()` still has some invalid arguments in the code above though. – john Aug 21 '14 at 08:42
  • Not according to MSDN, it doesn't. ;-) http://msdn.microsoft.com/en-us/library/bb299741%28v=vs.100%29.aspx – toadflakz Aug 21 '14 at 08:43
  • `testNM` is of type `XNamespace.` A _ToString_ then, I suppose. – john Aug 21 '14 at 09:33
  • I missed the `XNamespace` type there... Use `NamespaceName` property instead of `ToString()`. – toadflakz Aug 21 '14 at 09:38
  • Perfecto! Everything I could've asked for. – john Aug 21 '14 at 09:47
1

Your XML has default namespace. Descendants of the element where default namespace declared is considered in the same default namespace, unless it is explicitly declared with different namespace. That's why you need to use the same XNamespace for <key> element. :

var element = new XElement(testNM +"key",
                           new XAttribute("name", "someString"),
                           new XElement(testNM +"Type", type),
                           new XElement(testNM +"Value", value));
har07
  • 88,338
  • 12
  • 84
  • 137