Read from line X until line Y from Xaml with XamlXmlReader

360 Views Asked by At

I'm trying to call a third part API method which signature looks like

object Load(XamlXmlReader reader);

Then given this sample xaml

<Foo xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:barns="clr-namespace:Bar;assembly=Bar"
    Property="Value">
    <Root>
        <Element1 />
        <Element2>
            <SubElement>
                <barns:Sample />
            </SubElement>
        </Element2>
    </Root>
</Foo>

I need the to provide to the api a XamlXmlReader that load from, lets say, [line 7, column 12] until [line 9, column 25]

<SubElement>
    <barns:Sample />
</SubElement>

The final Xaml readed by the reader should be

<Foo xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:barns="clr-namespace:Bar;assembly=Bar"
    Property="Value">
        <SubElement>
            <barns:Sample />
        </SubElement>
</Foo>

Are there any function that does this kind of reading? If I have to roll my own, any suggestions or resources besides generating another file with this content manually from raw string, that may help?(I'm not familiar with XamlXmlReader) What is IXamlLineInfo and XamlXmlReaderSettings.ProvideLineInfo about?

Thanks

1

There are 1 best solutions below

0
On

This is the solution I found, it uses linq to XML, feel free to suggest improvements.

    public static XDocument CreateDocumentForLocation(Stream stream, Location targetLocation)
    {
        stream.Seek(0, 0);
        XElement root;
        List<XNode> nodesInLocation;
        XNamespace x = "http://schemas.microsoft.com/winfx/2006/xaml";
        using (var xmlReader = XmlReader.Create(stream, new XmlReaderSettings { 
            CloseInput = false }))
        {
            XDocument doc = XDocument.Load(xmlReader, 
                LoadOptions.SetLineInfo | LoadOptions.PreserveWhitespace);

            root = doc.Root;
            nodesInLocation = doc.Root.DescendantNodes()
                .Where(node => IsInside(node, targetLocation))
                .ToList();
        }

        root.RemoveNodes();
        XDocument trimmedDocument = XDocument.Load(root.CreateReader());
        trimmedDocument.Root.Add(nodesInLocation.FirstOrDefault());

        return trimmedDocument;
    }

    public static bool IsInside(XNode node, Location targetLocation)
    {
        var lineInfo = (IXmlLineInfo)node;
        return (lineInfo.LineNumber > targetLocation.StartLine && lineInfo.LineNumber < targetLocation.EndLine) // middle
            || (lineInfo.LineNumber == targetLocation.StartLine && lineInfo.LinePosition >= targetLocation.StartColumn) // first line after start column
            || (lineInfo.LineNumber == targetLocation.EndLine && lineInfo.LinePosition <= targetLocation.EndColumn); // last line until last column
    }

I needed to insert some other elements to the xml in my application. This is the core snippet, you would be able to use linq to xml to query easily anything you want to add to the final XML.