我对锈病很陌生,而且还在努力掌握使用它的诀窍。这很酷,但我显然错过了一些我给自己做的练习。作为参考,我使用的是rustc 1.39.0。
我想尝试编写一个简单的程序,从MSBuild的代码分析中读取XML,后者输出一些相当简单的XML。我认为的问题是,有一个元素(PATH
)通常是空的,但有时可以在它下面包含元素。更大的问题是,我对Rust (而且我通常不处理XML)很感兴趣,而且我也不知道如何正确设置反序列化所需的结构。我用的是Serde和quick_xml。当我将PATH
设置为字符串并使用路径下没有SFA元素的XML时,我的测试就成功了。但是,一旦我知道了应该如何使用该标记并相应地更新了我的结构,我就会不断地得到错误:
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文件都如下所示:
<?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
元素:
<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
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
[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" ] }
示例输出
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现在正在工作的代码中的更正:
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);
}
示例:
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包含:
<?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>
发布于 2019-12-23 09:55:22
PATH
本身应该被建模为一个带有一个可选字段的结构。这样做是可行的:
#[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>,
}
https://stackoverflow.com/questions/59452193
复制相似问题