使用Linq读取XML文件

3

概要:

我知道这是一篇很长的阅读材料。总结一下,我需要每种语言下不在documentation列中的名称、前缀和所有值。我还需要记录每个标签所属的语言。

我的问题是:

读取数据并将其保留在正确的列中——主要是如果出现空值,它会使一切都混乱了。在上面的Excel示例中,对于空白的日语标签,以下代码实际上会将gl-plt移动到label列中:

for(int t=0; t<labelLinkValues.Count; t+=labelLinkCol.Count) {

    for(int j=0; j<labelLinkCol.Count; j++) {
        if(labelLinkCol[j].Value=="prefix")
            Response.Write(labelLinkValues[t+j].Value+"<br/>");

        if(labelLinkCol[j].Value=="name")
            Response.Write(labelLinkValues[t+j].Value+"<br/>");

        for(int p=0; p<langList.Count; p++) {
            if(j>=langList[p].start&&j<=langList[p].end)
                if(labelLinkCol[j].Value!="documentation"&&labelLinkValues[j].Value!="")
                    Response.Write(langList[p].languageValue+"---"+labelLinkValues[t+j].Value+"<br/>");
        }

    }

    Response.Write("----<br>");
}

整个故事:

我正在尝试使用Linq读取一个以Excel导出的XML文件。我能够获取数据,但无法使其正常工作以将列与正确的行单元格对齐。

在发布代码之前,我应该提到一些事情。这个程序旨在读取任意数量的文件,它们可能有多种语言(第二行)。在这个特定的例子中,有两种语言,但在将来可能会有1种语言、3种语言等。语言的定义如下:

<Row ss:AutoFitHeight="0">
    <Cell ss:StyleID="ColumnHead2" ss:Index="4" ss:MergeAcross="1">
        <ss:Data ss:Type="String">en</ss:Data>
    </Cell>

    <Cell ss:StyleID="ColumnHead2" ss:MergeAcross="1">
        <ss:Data ss:Type="String">ja</ss:Data>
    </Cell>
</Row>

第一种语言的索引属性告诉您它从哪里开始,合并跨度告诉您它跨越了多少列。后续的语言仅包含合并跨越值,这些值告诉您每种语言之后跨越了多少列。

首先,这是两行示例数据的截图。

(下载的图像分辨率更高)enter image description here

以下是第一行对应的XML代码:

<Row ss:AutoFitHeight="0">
    <Cell ss:StyleID="NoBorderNumberCell">
        <ss:Data ss:Type="Number">1</ss:Data>
    </Cell>

    <Cell ss:StyleID="NoBorderCell">
        <ss:Data ss:Type="String">gl-cor</ss:Data>
    </Cell>

    <Cell ss:StyleID="NoBorderCell">
        <ss:Data ss:Type="String">account</ss:Data>
    </Cell>

    <Cell ss:StyleID="NoBorderCell">
        <ss:Data ss:Type="String">Tuple: parent container for account numbers and identifiers. No entry is made here, but each detail line may have multiple accounts assigned to it for reporting in different GAAPs, offsetting accounts in Japan</ss:Data>
    </Cell>

    <Cell ss:StyleID="NoBorderCell">
        <ss:Data ss:Type="String">Account Identifier</ss:Data>
    </Cell>

    <Cell ss:StyleID="NoBorderCell">
        <ss:Data ss:Type="String">タプル。勘定科目番号と識別子</ss:Data>
    </Cell>

    <Cell ss:StyleID="NoBorderCell"/>

    <Cell ss:StyleID="NoBorderCell">
        <ss:Data ss:Type="String">gl-plt</ss:Data>
    </Cell>
</Row>

我需要从给定的XML文件中提取3个值:

名称和前缀,它们不是语言的一部分,并且始终存在,获取它们不是问题。

任何在语言列下不包含documentation的值。因此,我需要忽略documentation

以下是我用来读取上述内容的代码:

XElement xdocument=XElement.Load(fpath);
XNamespace ns="urn:schemas-microsoft-com:office:spreadsheet";
XNamespace docProperties="urn:schemas-microsoft-com:office:office";
XNamespace ss="urn:schemas-microsoft-com:office:spreadsheet";
XNamespace search="ss";

var labelLinkCol=(
    from worksheets in xdocument.Elements(ns+"Worksheet")
    where (string)worksheets.Attribute(ss+"Name")=="Label Link"
    from columnHead in worksheets.Descendants(ns+"Cell")
    where (string)columnHead.Attribute(ss+"StyleID")=="ColumnHead"
    select columnHead
    ).ToList();

var langValues=(
    from worksheets in xdocument.Elements(ns+"Worksheet")
    where (string)worksheets.Attribute(ss+"Name")=="Label Link"
    from columnHead in worksheets.Descendants(ns+"Cell")
    where (string)columnHead.Attribute(ss+"StyleID")=="ColumnHead2"
    select columnHead
    ).ToList();

var labelLinkValues=(
    from worksheets in xdocument.Elements(ns+"Worksheet")
    where (string)worksheets.Attribute(ss+"Name")=="Label Link"
    from columnHead in worksheets.Descendants(ns+"Cell")
    where
        (string)columnHead.Attribute(ss+"StyleID")!="ColumnHead"
        &&
        (string)columnHead.Attribute(ss+"StyleID")!="ColumnHead2"
        &&
        (string)columnHead.Attribute(ss+"StyleID")!="ColumnHead2BL"
    select columnHead
    ).ToList();

int index=0;
List<language> langList=new List<language>();

for(int j=0; j<langValues.Count; j++) {
    language languageXML=new language();

    //the first value has the index
    if(j==0) {
        index=Convert.ToInt32(langValues[j].Attribute(ss+"Index").Value);
        languageXML.index=index;
        languageXML.start=index;
        languageXML.end=index+Convert.ToInt32(langValues[j].Attribute(ss+"MergeAcross").Value);
        languageXML.languageValue=langValues[j].Value;
    }
    else {
        //get the value of when the first language begins
        languageXML.index=index;

        //to get the beginning, get the end of the previous location and add 1
        languageXML.start=langList[langList.Count-1].end+1;

        //to get the last column, add the merge to the beginning
        languageXML.end=languageXML.start+Convert.ToInt32(langValues[j].Attribute(ss+"MergeAcross").Value);
        languageXML.languageValue=langValues[j].Value;
    }

    langList.Add(languageXML);
    //Response.Write(languageXML.start + "--" + languageXML.end + "--" + languageXML.languageValue + "<br>");
}

language 是一个简单的类,它只包含第一种语言的列,当前语言的起始列和当前语言的结束列。

我相信以上的代码是正确的。它可以读取所有值,并正确识别每种语言的起始和结束位置,问题就像帖子前面描述的那样。


这个 XML 格式太糟糕了。你不能以一些更好的格式获取数据吗? - svick
你尝试过使用 xsd.exe 吗?如果你只是读取所有内容,那么如果它可以正确解析,这可能是一种更简单的方法。 - jle
很不幸,没有其他方法可以获取数据,它是由客户从Excel工作表中导出的。数据读取得很好,只是处理空白在逻辑上让我很困扰。 - john cs
@jle 我对 XSD 的理解是,它用于当你有一个 XML 文件但没有模式的情况下。对于这种目的,结构主要始终保持不变,只是语言上有所不同。 - john cs
xsd.exe是Visual Studio开发工具,它可以为您创建C#类,然后您可以将XML反序列化为C#强类型类 - http://dotnetdust.blogspot.com/2010/05/correctly-creating-classes-using-xsdexe.html - jle
1个回答

0

看起来你把它想得比它需要的更复杂了。我最近在一个项目上工作,需要用户从Excel中复制数据,该数据通过剪贴板作为XML电子表格读取,然后经过解析器转换为表格式。

我发现XML电子表格参考非常有用: http://msdn.microsoft.com/en-us/library/office/aa140066(v=office.10).aspx

你需要注意的一件事,并且我认为这是你问题的原因,就是ss:Index不是以零为基础的(第一列=索引1)。

这应该非常简单,只需循环遍历行及其单元格,并跟踪单元格的索引即可。您还可以检查所需单元格是否具有子数据元素。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接