0

I'm using this code to convert from an XElement to OpenXmlElement

    internal static OpenXmlElement ToOpenXml(this XElement xel)
    {
        using (var sw = new StreamWriter(new MemoryStream()))
        {
            sw.Write(xel.ToString());
            sw.Flush();
            sw.BaseStream.Seek(0, SeekOrigin.Begin);

            var re = OpenXmlReader.Create(sw.BaseStream);
            re.Read();

            var oxe = re.LoadCurrentElement();
            re.Close();
            return oxe;
        }
    }

Before the conversion I have an XElement

    <w:ind w:firstLine="0" w:left="0" w:right="0"/>    

After the conversion it looks like this

    <w:ind w:firstLine="0" w:end="0" w:start="0"/>

This element then fails OpenXml validation using the following

    var v = new OpenXmlValidator();
    var errs = v.Validate(doc);

With the errors being reported:

    Description="The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:start' attribute is not declared."
    Description="The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:end' attribute is not declared."

Do I need to do other things to add these attributes to the schema or do I need to find a new way to convert from XElement to OpenXml?

I'm using the nuget package DocumentFormat.OpenXml ver 2.9.1 (the latest).

EDIT: Looking at the OpenXml standard, it seems that both left/start and right/end should be recognised which would point to the OpenXmlValidator not being quite correct. Presumably I can just ignore those validation errors then?

Many thx

Simon Woods
  • 905
  • 1
  • 6
  • 13
  • Can you provide more information on the `XElement` that you passed to your `ToOpenXml` method? Passing a `w:ind` `XElement` yields an `OpenXmlUnknownElement` and the attributes are not changed. Passing a `w:document` `XElement` yields a `Document` and the `Indentation` descendant's attributes are changed as you described. – Thomas Barnekow Dec 21 '19 at 10:59

1 Answers1

1

The short answer is that you can indeed ignore those specific validation errors. The OpenXmlValidator is not up-to-date in this case.

I would additionally offer a more elegant implementation of your ToOpenXml method (note the using declarations, which were added in C# 8.0).

internal static OpenXmlElement ToOpenXmlElement(this XElement element)
{
    // Write XElement to MemoryStream.
    using var stream = new MemoryStream();
    element.Save(stream);
    stream.Seek(0, SeekOrigin.Begin);

    // Read OpenXmlElement from MemoryStream.
    using OpenXmlReader reader = OpenXmlReader.Create(stream);
    reader.Read();
    return reader.LoadCurrentElement();
}

If you don't use C# 8.0 or using declarations, here's the corresponding code with using statements.

internal static OpenXmlElement ToOpenXmlElement(this XElement element)
{
    using (var stream = new MemoryStream())
    {
        // Write XElement to MemoryStream.
        element.Save(stream);
        stream.Seek(0, SeekOrigin.Begin);

        // Read OpenXmlElement from MemoryStream.
        using OpenXmlReader reader = OpenXmlReader.Create(stream);
        {
            reader.Read();
            return reader.LoadCurrentElement();
        }
    }
}

Here's the corresponding unit test, which also demonstrates that you'd have to pass a w:document to have the w:ind element's attributes changed by the Indentation instance created in the process.

public class OpenXmlReaderTests
{
    private const string NamespaceUriW = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
    private static readonly string XmlnsW = $"xmlns:w=\"{NamespaceUriW}\"";

    private static readonly string IndText =
        $@"<w:ind {XmlnsW} w:firstLine=""10"" w:left=""20"" w:right=""30""/>";

    private static readonly string DocumentText =
        $@"<w:document {XmlnsW}><w:body><w:p><w:pPr>{IndText}</w:pPr></w:p></w:body></w:document>";

    [Fact]
    public void ConvertingDocumentChangesIndProperties()
    {
        XElement element = XElement.Parse(DocumentText);

        var document = (Document) element.ToOpenXmlElement();
        Indentation ind = document.Descendants<Indentation>().First();

        Assert.Null(ind.Left);
        Assert.Null(ind.Right);

        Assert.Equal("10", ind.FirstLine);
        Assert.Equal("20", ind.Start);
        Assert.Equal("30", ind.End);
    }

    [Fact]
    public void ConvertingIndDoesNotChangeIndProperties()
    {
        XElement element = XElement.Parse(IndText);

        var ind = (OpenXmlUnknownElement) element.ToOpenXmlElement();

        Assert.Equal("10", ind.GetAttribute("firstLine", NamespaceUriW).Value);
        Assert.Equal("20", ind.GetAttribute("left", NamespaceUriW).Value);
        Assert.Equal("30", ind.GetAttribute("right", NamespaceUriW).Value);
    }
}
Thomas Barnekow
  • 2,059
  • 1
  • 12
  • 21