首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >难以使用Rust和Serde反序列化XML,其中文档具有可选的子元素

难以使用Rust和Serde反序列化XML,其中文档具有可选的子元素
EN

Stack Overflow用户
提问于 2019-12-23 08:31:24
回答 1查看 2K关注 0票数 2

我对锈病很陌生,而且还在努力掌握使用它的诀窍。这很酷,但我显然错过了一些我给自己做的练习。作为参考,我使用的是rustc 1.39.0。

我想尝试编写一个简单的程序,从MSBuild的代码分析中读取XML,后者输出一些相当简单的XML。我认为的问题是,有一个元素(PATH)通常是空的,但有时可以在它下面包含元素。更大的问题是,我对Rust (而且我通常不处理XML)很感兴趣,而且我也不知道如何正确设置反序列化所需的结构。我用的是Serde和quick_xml。当我将PATH设置为字符串并使用路径下没有SFA元素的XML时,我的测试就成功了。但是,一旦我知道了应该如何使用该标记并相应地更新了我的结构,我就会不断地得到错误:

代码语言:javascript
运行
复制
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom("missing field `FILEPATH`")', src\libcore\result.rs:1165:5

...even如果测试XML文件中的所有缺陷在PATH下都有SFA元素。

我处理的所有XML文件都如下所示:

代码语言:javascript
运行
复制
<?xml version="1.0" encoding="utf-8"?>
<DEFECTS>
  <DEFECT>
    <SFA>
      <FILEPATH>c:\projects\source\repos\defecttest\defecttest</FILEPATH>
      <FILENAME>source.cpp</FILENAME>
      <LINE>8</LINE>
      <COLUMN>5</COLUMN>
    </SFA>
    <DEFECTCODE>26496</DEFECTCODE>
    <DESCRIPTION>The variable 'y' is assigned only once, mark it as const (con.4).</DESCRIPTION>
    <FUNCTION>main</FUNCTION>
    <DECORATED>main</DECORATED>
    <FUNCLINE>6</FUNCLINE>
    <PATH></PATH>
  </DEFECT>
  <DEFECT>
    <SFA>
      <FILEPATH>c:\projects\source\repos\defecttest\defecttest</FILEPATH>
      <FILENAME>source.cpp</FILENAME>
      <LINE>9</LINE>
      <COLUMN>5</COLUMN>
    </SFA>
    <DEFECTCODE>26496</DEFECTCODE>
    <DESCRIPTION>The variable 'z' is assigned only once, mark it as const (con.4).</DESCRIPTION>
    <FUNCTION>main</FUNCTION>
    <DECORATED>main</DECORATED>
    <FUNCLINE>6</FUNCLINE>
    <PATH></PATH>
  </DEFECT>
</DEFECTS>

在许多情况下,PATH是空的,但在有些情况下,它包含自己的SFA元素:

代码语言:javascript
运行
复制
  <DEFECT>
    <SFA>
      <FILEPATH>c:\projects\source\repos\defecttest\defecttest</FILEPATH>
      <FILENAME>source.cpp</FILENAME>
      <LINE>9</LINE>
      <COLUMN>5</COLUMN>
    </SFA>
    <DEFECTCODE>26496</DEFECTCODE>
    <DESCRIPTION>The variable 'z' is assigned only once, mark it as const (con.4).</DESCRIPTION>
    <FUNCTION>main</FUNCTION>
    <DECORATED>main</DECORATED>
    <FUNCLINE>6</FUNCLINE>
    <PATH>
      <SFA>
        <FILEPATH>c:\projects\source\repos\defecttest\defecttest</FILEPATH>
        <FILENAME>source.cpp</FILENAME>
        <LINE>12</LINE>
        <COLUMN>3</COLUMN>
      </SFA>
    </PATH>
  </DEFECT>

在我意识到这一点之前,缺陷结构中的所有字段都被设置为String。这是正确的,假设XML文件中的任何缺陷都没有子元素在PATH下。当我将它改为SFA而不是String时,它会给出上面提到的丢失字段错误。我正在测试的代码示例:

main.rs

代码语言:javascript
运行
复制
extern crate quick_xml;
extern crate serde;

use std::default::Default;
use std::env;
use std::vec::Vec;

use quick_xml::de::from_str;
use serde::{Serialize, Deserialize};

/*
 * Structs for the defect XML
 */

#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct DEFECTS {
    #[serde(rename = "DEFECT", default)]
    pub defects: Vec<DEFECT>,
}

#[derive(Default, Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct DEFECT {
    #[serde(default)]
    pub SFA: SFA,
    pub DEFECTCODE: String,
    pub DESCRIPTION: String,
    pub FUNCTION: String,
    pub DECORATED: String,
    pub FUNCLINE: String,
    #[serde(default)]
    pub PATH: Vec<SFA>,
}

#[derive(Default, Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct SFA {
    pub FILEPATH: String,
    pub FILENAME: String,
    pub LINE: String,
    pub COLUMN: String,
}

/*
 * Main app code
 */

fn main() {
    // Expect the path to the XML file to be passed as the first and only argument
    let args: Vec<String> = env::args().collect();
    if args.len() != 2 {
        panic!("Invalid argument count. Specify a single file to process.");
    }

    let processing_file = &args[1];
    println!("Will attempt to process file: '{}'", &processing_file);

    // Try to load the contents of the file
    let file_content : String = match std::fs::read_to_string(&processing_file) {
        Ok(file_content) => file_content,
        Err(e) => {
            panic!("Failed to read file: '{}' -- {}", &processing_file, e);
        }
    };

    // Now, try to deserialize the XML we have in file_content
    let defect_list : DEFECTS = from_str(&file_content).unwrap();

    // Assuming the unwrap above didn't blow up, we should get a count here
    println!("Retrieved {} defects from file '{}'", defect_list.defects.len(), &processing_file);
}

Cargo.toml

代码语言:javascript
运行
复制
[package]
name = "rust_xml_test"
version = "0.1.0"
authors = ["fny82"]
edition = "2018"

[dependencies]
quick-xml = { version = "0.17", features = [ "serialize" ] }
serde = { version = "1.0", features = [ "derive" ] }

示例输出

代码语言:javascript
运行
复制
C:\Development\RustXmlTest>cargo run -- "c:\development\rustxmltest\test3.xml"
   Compiling rust_xml_test v0.1.0 (C:\Development\RustXmlTest)
    Finished dev [unoptimized + debuginfo] target(s) in 1.56s
     Running `target\debug\rust_xml_test.exe c:\development\rustxmltest\test3.xml`
Will attempt to process file: 'c:\development\rustxmltest\test3.xml'
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom("missing field `FILEPATH`")', src\libcore\result.rs:1165:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
error: process didn't exit successfully: `target\debug\rust_xml_test.exe c:\development\rustxmltest\test3.xml` (exit code: 101)

我肯定我在做一些愚蠢的事情,其中一部分原因可能是我在挑战的范围和我目前对使用Rust的理解程度上超越了我自己。有人能帮我找出我错过了什么,做错什么吗?

有些关联:从那以后,我了解到我可以使用rename属性来使我的结构符合Rust的命名约定,但现在我不想开始处理这个问题,直到底层功能开始工作为止。

--编辑--

请参阅@edwardw现在正在工作的代码中的更正:

代码语言:javascript
运行
复制
extern crate quick_xml;
extern crate serde;

use std::default::Default;
use std::env;
use std::vec::Vec;

use quick_xml::de::from_str;
use serde::{Serialize, Deserialize};

/*
 * Structs for the defect XML
 */

#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct DEFECTS {
    #[serde(rename = "DEFECT", default)]
    pub defects: Vec<DEFECT>,
}

#[derive(Default, Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct DEFECT {
    #[serde(default)]
    pub SFA: SFA,
    pub DEFECTCODE: String,
    pub DESCRIPTION: String,
    pub FUNCTION: String,
    pub DECORATED: String,
    pub FUNCLINE: String,
    pub PATH: PATH,
}

#[derive(Default, Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct SFA {
    pub FILEPATH: String,
    pub FILENAME: String,
    pub LINE: String,
    pub COLUMN: String,
}

#[derive(Default, Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct PATH {
    pub SFA: Option<SFA>,
}

/*
 * Main app code
 */

fn main() {
    // Expect the path to the XML file to be passed as the first and only argument
    let args: Vec<String> = env::args().collect();
    if args.len() != 2 {
        panic!("Invalid argument count. Specify a single file to process.");
    }

    let processing_file = &args[1];
    println!("Will attempt to process file: '{}'", &processing_file);

    // Try to load the contents of the file
    let file_content : String = match std::fs::read_to_string(&processing_file) {
        Ok(file_content) => file_content,
        Err(e) => {
            panic!("Failed to read file: '{}' -- {}", &processing_file, e);
        }
    };

    // Now, try to deserialize the XML we have in file_content
    let defect_list : DEFECTS = from_str(&file_content).unwrap();

    // Assuming the unwrap above didn't blow up, we should get a count here
    println!("Retrieved {} defects from file '{}'", defect_list.defects.len(), &processing_file);
}

示例:

代码语言:javascript
运行
复制
C:\Development\RustXmlTest>cargo run -- "c:\development\rustxmltest\test1.xml"
   Compiling rust_xml_test v0.1.0 (C:\Development\RustXmlTest)
    Finished dev [unoptimized + debuginfo] target(s) in 1.66s
     Running `target\debug\rust_xml_test.exe c:\development\rustxmltest\test1.xml`
Will attempt to process file: 'c:\development\rustxmltest\test1.xml'
Retrieved 2 defects from file 'c:\development\rustxmltest\test1.xml'

其中test1.xml包含:

代码语言:javascript
运行
复制
<?xml version="1.0" encoding="utf-8"?>
<DEFECTS>
  <DEFECT>
    <SFA>
      <FILEPATH>c:\projects\source\repos\defecttest\defecttest</FILEPATH>
      <FILENAME>source.cpp</FILENAME>
      <LINE>8</LINE>
      <COLUMN>5</COLUMN>
    </SFA>
    <DEFECTCODE>26496</DEFECTCODE>
    <DESCRIPTION>The variable 'y' is assigned only once, mark it as const (con.4).</DESCRIPTION>
    <FUNCTION>main</FUNCTION>
    <DECORATED>main</DECORATED>
    <FUNCLINE>6</FUNCLINE>
    <PATH></PATH>
  </DEFECT>
  <DEFECT>
    <SFA>
      <FILEPATH>c:\projects\source\repos\defecttest\defecttest</FILEPATH>
      <FILENAME>source.cpp</FILENAME>
      <LINE>9</LINE>
      <COLUMN>5</COLUMN>
    </SFA>
    <DEFECTCODE>26496</DEFECTCODE>
    <DESCRIPTION>The variable 'z' is assigned only once, mark it as const (con.4).</DESCRIPTION>
    <FUNCTION>main</FUNCTION>
    <DECORATED>main</DECORATED>
    <FUNCLINE>6</FUNCLINE>
    <PATH>
      <SFA>
        <FILEPATH>c:\projects\source\repos\defecttest\defecttest</FILEPATH>
        <FILENAME>source.cpp</FILENAME>
        <LINE>12</LINE>
        <COLUMN>3</COLUMN>
      </SFA>
    </PATH>
  </DEFECT>
</DEFECTS>
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-12-23 09:55:22

PATH本身应该被建模为一个带有一个可选字段的结构。这样做是可行的:

代码语言:javascript
运行
复制
#[derive(Default, Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct DEFECT {
    #[serde(default)]
    pub SFA: SFA,
    pub DEFECTCODE: String,
    pub DESCRIPTION: String,
    pub FUNCTION: String,
    pub DECORATED: String,
    pub FUNCLINE: String,
    pub PATH: PATH,
}

#[derive(Default, Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct PATH {
    SFA: Option<SFA>,
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59452193

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档