有没有一种方法可以将多个XElement序列化到同一行上? [英] Is there a way to serialize multiple XElements onto the same line?

查看:54
本文介绍了有没有一种方法可以将多个XElement序列化到同一行上?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在处理Silverlight 3中令人恐惧的<Run/>,并且必须以编程方式创建<TextBlock>及其内联:

I'm dealing with the dreaded <Run/> in Silverlight 3 and having to programmatically create a <TextBlock> and its inlines:

为什么害怕?我猜这是行不通的,我想这是您期望的方式.下面的图表A应该产生

Why dreaded? Because it doesn't work, I guess, the way you'd expect. Exhibit A, below, should produce

BARN

(每个字符带有奇特的颜色),但产生的却是:

(with fancy colors for each character), but instead it produces:

B A R N

<TextBlock FontFamily="Comic Sans MS" FontSize="88">
    <Run Foreground="#A200FF">B</Run>
    <Run Foreground="#FF0000">A</Run>
    <Run Foreground="#FFC000">R</Run>
    <Run Foreground="#FFFF00">N</Run>
</TextBlock>

可产生预期结果的是:

<TextBlock FontFamily="Comic Sans MS" FontSize="88">
    <Run Foreground="#A200FF">B</Run><Run Foreground="#FF0000">A</Run><Run Foreground="#FFC000">R</Run><Run Foreground="#FFFF00">N</Run>
</TextBlock>

愚蠢,是吗?无论如何,这已记录在@ Silverlight之间的XAML处理差异空白处理下的"3"和"Silverlight 4" 表示:

Stupid, eh? Anyway, this is documented @ XAML Processing Differences Between Silverlight 3 and Silverlight 4 under Whitespace Handling where it says:

Silverlight 3更对待空白 实际上,范围更广,包括 在某些情况下,考虑使用CLRF 重大.这有时导致 XAML中省略了CRLF的文件格式XAML 为了避免不必要的空格 演示文稿,但不是 在编辑中易于阅读 环境. Silverlight 4使用了 更直观的有效空白 与WPF相似的模型.这 模型折叠文件格式 在大多数情况下,空白 某些CLR归因的例外 处理所有空白的容器 一样重要.这个空白模型 提供更好的编辑环境 引入空白的自由 可以改善XAML代码格式. 此外,Silverlight 4具有文本元素 可以更好地控制 空白演示文稿问题.

Silverlight 3 treats whitespace more literally in a wider range, including some cases where CLRF is considered significant. This sometimes led to file-format XAML with omitted CRLF in order to avoid unwanted whitespace in the presentation, but which was not human-readable in editing environments. Silverlight 4 uses a more intuitive significant-whitespace model that is similar to WPF. This model collapses file-formatting whitespace in most cases, with exception of certain CLR-attributed containers that treat all whitespace as significant. This whitespace model gives editing environments greater freedom to introduce whitespace that can improve XAML code formatting. Also, Silverlight 4 has text elements that permit even greater control over whitespace presentation issues.

太好了,但是我没有使用SL4,因为我正在以编程方式创建WP7应用.是的,我的XAML已生成.使用XML文字.然后发送到一个字符串.像这样:

Great, but I'm not using SL4 because I'm creating a WP7 app programmatically. Yeah, my XAML is generated. Using XML Literals. Then sent to a string. Like this:

Dim r1 As XElement = <Run Foreground="#A200FF">B</Run>
Dim r2 As XElement = <Run Foreground="#FF0000">A</Run>
Dim r3 As XElement = <Run Foreground="#FFC000">R</Run>
Dim r4 As XElement = <Run Foreground="#FFFF00">N</Run>
Dim tb = <TextBlock FontFamily="Comic Sans MS" FontSize="88">
             <%= r1 %><%= r2 %><%= r3 %><%= r4 %>
         </TextBlock>
Dim result = tb.ToString

所有这些之后,这是我的问题:如何生成附件B而不是附件A.该文本块将成为XAML页面中更多元素的一部分,因此.ToString部分在此位置-当用户控制页面的所有XAML被踢出文件时就会发生.

After all this, here's my question: How can I produce Exhibit B instead of Exhibit A. That textblock will become part of a greater number of elements in a XAML page, so the .ToString part isn't exactly accurate in this location - that happens when all of the XAML for the user control page is kicked out to file.

我在如下方面取得了一些进步,但是我在这里遇到了一个难题:如何完成不寻常的拆分和处理XML以输出字符串.举一个新的例子:

I've made a bit of progress as below, but I'm running up against a mental block here on how to accomplish an unusual split and processing of XML to output a string. Take this new example:

<Canvas>
  <Grid>
    <TextBlock>
      <Run Text="r"/>
      <Run Text="u"/>
      <Run Text="n"/>
    </TextBlock>
    <TextBlock>
      <Run Text="far a"/>
      <Run Text="way"/>
      <Run Text=" from me"/>
    </TextBlock>
  </Grid>
  <Grid>
    <TextBlock>
      <Run Text="I"/>
      <Run Text=" "/>
      <Run Text="want"/>
      <LineBreak/>
    </TextBlock>
    <TextBlock>
      <LineBreak/>
      <Run Text="...thi"/>
      <Run Text="s to"/>
      <LineBreak/>
      <Run Text=" work"/>
    </TextBlock>
  </Grid>
</Canvas>

我希望输出字符串为:

<Canvas>
  <Grid>
    <TextBlock>
      <Run Text="r"/><Run Text="u"/><Run Text="n"/>
    </TextBlock>
    <TextBlock>
      <Run Text="far a"/><Run Text="way"/><Run Text=" from me"/>
    </TextBlock>
  </Grid>
  <Grid>
    <TextBlock>
      <Run Text="I"/><Run Text=" "/><Run Text="want"/>
      <LineBreak/>
    </TextBlock>
    <TextBlock>
      <LineBreak/>
      <Run Text="...thi"/><Run Text="s to"/>
      <LineBreak/>
      <Run Text=" work"/>
    </TextBlock>
  </Grid>
</Canvas>

我一直在根据

I've been looking at the XMLWriter and XMLWriterSettings, based on Eric White's post, which seems to be a good start for the runs (not including the potential <LineBreak/>s yet, which also stumps me). Like this:

Sub Main()
    Dim myXML As XElement = <Canvas>
                                <Grid>
                                    <TextBlock>
                                        <Run Text="r"/>
                                        <Run Text="u"/>
                                        <Run Text="n"/>
                                    </TextBlock>
                                    <TextBlock>
                                        <Run Text="far a"/>
                                        <Run Text="way"/>
                                        <Run Text=" from me"/>
                                    </TextBlock>
                                </Grid>
                            </Canvas>
    Console.Write(ToXMLString(myXML))
    Console.ReadLine()
End Sub
Public Function ToXMLString(xml As XElement) As String
    Dim tb As XElement = xml.Elements.<TextBlock>.FirstOrDefault
    Dim xmlWriterSettings As New XmlWriterSettings
    XmlWriterSettings.NewLineHandling = NewLineHandling.None
    XmlWriterSettings.OmitXmlDeclaration = True
    Dim sb As New StringBuilder
    Using xmlwriter As XmlWriter = xmlwriter.Create(sb, XmlWriterSettings)
        tb.WriteTo(xmlwriter)
    End Using
    Return sb.ToString
End Function

但是在弄清楚如何解析它以产生上面期望的输出时,我还有一个更大的问题.

But I'm having a huge problem going much further with figuring out how to parse this to produce the desired output above.

推荐答案

解决此问题的关键是编写一个遍历XML树的递归函数,将各种元素和属性写入专门创建的XmlWriter对象.有一个写入缩进XML的外部" XmlWriter对象和一个写入缩进XML的内部" XmlWriter对象.

The key to solving this problem is to write a recursive function that iterates through the XML tree, writing the various elements and attributes to specially created XmlWriter objects. There is an 'outer' XmlWriter object that writes indented XML, and an 'inner' XmlWriter object that writes non-indented XML.

递归函数最初使用外部" XmlWriter,编写缩进的XML,直到看到TextBlock元素为止.当遇到TextBlock元素时,它将创建内部" XmlWriter对象,并将TextBlock元素的子元素写入该对象.它还将空白写入内部" XmlWriter.

The recursive function initially uses the 'outer' XmlWriter, writing indented XML, until it sees the TextBlock element. When it encounters the TextBlock element, it creates the 'inner' XmlWriter object, writing the child elements of the TextBlock element to it. It also writes white space to the 'inner' XmlWriter.

在完成内部" XmlWriter对象的写入TextBlock元素后,使用WriteRaw方法将编写者编写的文本写入外部" XmlWriter.

When the 'inner' XmlWriter object is finished with writing the TextBlock element, the text that the writer wrote is written to the 'outer' XmlWriter using the WriteRaw method.

此方法的优点是没有XML的后处理.对XML进行后处理是非常困难的,并且要确定您已经正确处理了所有情况,包括CData节点中的任意文本等.所有XML仅使用XmlWriter类编写,从而确保将始终写入有效的XML. .唯一的例外是使用WriteRaw方法编写的特制空白,可以实现所需的缩进行为.

The advantages of this approach is that there is no post-processing of the XML. It is extremely difficult to post-process XML and be certain that you have properly handled all cases, including arbitrary text in CData nodes, etc. All of the XML is written using only the XmlWriter class, thereby ensuring that this will always write valid XML. The only exception to this is the specially crafted white-space that is written using the WriteRaw method, which achieves the desired indenting behavior.

一个关键点是,内部" XmlWriter对象的一致性级别设置为ConformanceLevel.Fragment,因为内部" XmlWriter需要编写没有根元素的XML.

One key point is that the 'inner' XmlWriter object's conformance level is set to ConformanceLevel.Fragment, because the 'inner' XmlWriter needs to write XML that does not have a root element.

为实现所需的Run元素格式(即,相邻的Run元素之间没有可忽略的空白),代码使用GroupAdjacent扩展方法.前段时间,我在

To achieve the desired formatting of Run elements (i.e. Run elements that are adjacent have no insignificant white space between them), the code uses the GroupAdjacent extension method. Some time ago, I write a blog post on the GroupAdjacent extension method for VB.

使用指定的示例XML运行代码时,其输出:

When you run the code using the specified sample XML, it outputs:

<Canvas>
  <Grid>
    <TextBlock>
      <Run Text="r" /><Run Text="u" /><Run Text="n" />
    </TextBlock>
    <TextBlock>
      <Run Text="far a" /><Run Text="way" /><Run Text=" from me" />
    </TextBlock>
  </Grid>
  <Grid>
    <TextBlock>
      <Run Text="I" /><Run Text=" " /><Run Text="want" />
      <LineBreak />
    </TextBlock>
    <TextBlock>
      <LineBreak />
      <Run Text="...thi" /><Run Text="s to" />
      <LineBreak />
      <Run Text=" work" />
    </TextBlock>
  </Grid>
</Canvas>

以下是VB.NET示例程序的完整列表.此外,我写了一篇博客文章

Following is the complete listing of the VB.NET example program. In addition, I've written a blog post, Custom Formatting of XML using LINQ to XML, which presents the equivalent C# code.

`

Imports System.Text
Imports System.Xml

Public Class GroupOfAdjacent(Of TElement, TKey)
    Implements IEnumerable(Of TElement)

    Private _key As TKey
    Private _groupList As List(Of TElement)

    Public Property GroupList() As List(Of TElement)
        Get
            Return _groupList
        End Get
        Set(ByVal value As List(Of TElement))
            _groupList = value
        End Set
    End Property

    Public ReadOnly Property Key() As TKey
        Get
            Return _key
        End Get
    End Property

    Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of TElement) _
            Implements System.Collections.Generic.IEnumerable(Of TElement).GetEnumerator
        Return _groupList.GetEnumerator
    End Function

    Public Function GetEnumerator1() As System.Collections.IEnumerator _
            Implements System.Collections.IEnumerable.GetEnumerator
        Return _groupList.GetEnumerator
    End Function

    Public Sub New(ByVal key As TKey)
        _key = key
        _groupList = New List(Of TElement)
    End Sub
End Class

Module Module1
    <System.Runtime.CompilerServices.Extension()> _
    Public Function GroupAdjacent(Of TElement, TKey)(ByVal source As IEnumerable(Of TElement), _
                ByVal keySelector As Func(Of TElement, TKey)) As List(Of GroupOfAdjacent(Of TElement, TKey))
        Dim lastKey As TKey = Nothing
        Dim currentGroup As GroupOfAdjacent(Of TElement, TKey) = Nothing
        Dim allGroups As List(Of GroupOfAdjacent(Of TElement, TKey)) = New List(Of GroupOfAdjacent(Of TElement, TKey))()
        For Each item In source
            Dim thisKey As TKey = keySelector(item)
            If lastKey IsNot Nothing And Not thisKey.Equals(lastKey) Then
                allGroups.Add(currentGroup)
            End If
            If Not thisKey.Equals(lastKey) Then
                currentGroup = New GroupOfAdjacent(Of TElement, TKey)(keySelector(item))
            End If
            currentGroup.GroupList.Add(item)
            lastKey = thisKey
        Next
        If lastKey IsNot Nothing Then
            allGroups.Add(currentGroup)
        End If
        Return allGroups
    End Function

    Public Sub WriteStartElement(ByVal writer As XmlWriter, ByVal e As XElement)
        Dim ns As XNamespace = e.Name.Namespace
        writer.WriteStartElement(e.GetPrefixOfNamespace(ns), _
            e.Name.LocalName, ns.NamespaceName)
        For Each a In e.Attributes
            ns = a.Name.Namespace
            Dim localName As String = a.Name.LocalName
            Dim namespaceName As String = ns.NamespaceName
            writer.WriteAttributeString( _
                e.GetPrefixOfNamespace(ns), _
                localName, _
                IIf(namespaceName.Length = 0 And localName = "xmlns", _
                    XNamespace.Xmlns.NamespaceName, namespaceName),
                a.Value)
        Next
    End Sub

    Public Sub WriteElement(ByVal writer As XmlWriter, ByVal e As XElement)
        If (e.Name = "TextBlock") Then
            WriteStartElement(writer, e)
            writer.WriteRaw(Environment.NewLine)

            ' Create an XML writer that outputs no insignificant white space so that we can
            ' write to it and explicitly control white space.
            Dim settings As XmlWriterSettings = New XmlWriterSettings()
            settings.Indent = False
            settings.OmitXmlDeclaration = True
            settings.ConformanceLevel = ConformanceLevel.Fragment
            Dim sb As StringBuilder = New StringBuilder()
            Using newXmlWriter As XmlWriter = XmlWriter.Create(sb, settings)
                ' Group adjacent runs so that they can be output with no whitespace between them
                Dim groupedRuns = e.Nodes().GroupAdjacent( _
                    Function(n) As Boolean?
                        If TypeOf n Is XElement Then
                            Dim element As XElement = n
                            If element.Name = "Run" Then
                                Return True
                            End If
                            Return False
                        End If
                        Return False
                    End Function)
                For Each g In groupedRuns
                    If g.Key = True Then
                        ' Write white space so that the line of Run elements is properly indented.
                        newXmlWriter.WriteRaw("".PadRight((e.Ancestors().Count() + 1) * 2))
                        For Each run In g
                            run.WriteTo(newXmlWriter)
                        Next
                        newXmlWriter.WriteRaw(Environment.NewLine)
                    Else
                        For Each g2 In g
                            ' Write some white space so that each child element is properly indented.
                            newXmlWriter.WriteRaw("".PadRight((e.Ancestors().Count() + 1) * 2))
                            g2.WriteTo(newXmlWriter)
                            newXmlWriter.WriteRaw(Environment.NewLine)
                        Next
                    End If
                Next
            End Using
            writer.WriteRaw(sb.ToString())
            writer.WriteRaw("".PadRight(e.Ancestors().Count() * 2))
            writer.WriteEndElement()
        Else
            WriteStartElement(writer, e)
            For Each n In e.Nodes
                If TypeOf n Is XElement Then
                    Dim element = n
                    WriteElement(writer, element)
                    Continue For
                End If
                n.WriteTo(writer)
            Next
            writer.WriteEndElement()
        End If
    End Sub

    Function ToStringWithCustomWhiteSpace(ByVal element As XElement) As String
        ' Create XmlWriter that indents.
        Dim settings As XmlWriterSettings = New XmlWriterSettings()
        settings.Indent = True
        settings.OmitXmlDeclaration = True
        Dim sb As StringBuilder = New StringBuilder()
        Using xmlWriter As XmlWriter = xmlWriter.Create(sb, settings)
            WriteElement(xmlWriter, element)
        End Using
        Return sb.ToString()
    End Function

    Sub Main()
        Dim myXML As XElement = _
            <Canvas>
                <Grid>
                    <TextBlock>
                        <Run Text='r'/>
                        <Run Text='u'/>
                        <Run Text='n'/>
                    </TextBlock>
                    <TextBlock>
                        <Run Text='far a'/>
                        <Run Text='way'/>
                        <Run Text=' from me'/>
                    </TextBlock>
                </Grid>
                <Grid>
                    <TextBlock>
                        <Run Text='I'/>
                        <Run Text=' '/>
                        <Run Text='want'/>
                        <LineBreak/>
                    </TextBlock>
                    <TextBlock>
                        <LineBreak/>
                        <Run Text='...thi'/>
                        <Run Text='s to'/>
                        <LineBreak/>
                        <Run Text=' work'/>
                    </TextBlock>
                </Grid>
            </Canvas>
        Console.Write(ToStringWithCustomWhiteSpace(myXML))
        Console.ReadLine()
    End Sub

End Module

`

这篇关于有没有一种方法可以将多个XElement序列化到同一行上?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆