Code Monkey home page Code Monkey logo

xmlserde's People

Contributors

bilelmoussaoui avatar dependabot[bot] avatar imjeremyhe avatar speedy37 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

xmlserde's Issues

Github url on Crates.io incorrect

The url registered with crates.io points to .... github.com/proclml/LogiSheets/tree/master/crates/xmlserde

The url was not updated when the xmlserde crate was removed from the LogiSheets project.

Support tags containing child, text or both

Hi Jeremy, thanks for great crate.

I am trying to deserialize following xml:

<office:document-content>
    <office:body>
        <office:text text:use-soft-page-breaks="true">
            <text:p text:style-name="Normal">
                <text:span text:style-name="T2">Progress:<text:s/>
                </text:span>
                <text:span text:style-name="T3">
                    <office:annotation office:name="0" xml:id="1825723351">
                        <dc:creator>Name Surname</dc:creator>
                        <dc:date>2024-03-12T10:30:00</dc:date>
                        <meta:creator-initials>NS</meta:creator-initials>
                        <text:p text:style-name="CommentText">progress indicator</text:p>
                    </office:annotation>100%</text:span>
                <text:span text:style-name="CommentReference">
                    <office:annotation-end office:name="0"/>
                </text:span>
            </text:p>
            <text:p text:style-name="P4">Task completed!</text:p>
            <text:p text:style-name="P5"/>
        </office:text>
    </office:body>
</office:document-content>

Yes, I know, it has nothing in common with LogiSheets, but your crate works almost perfectly even for ODT file.

The problem is with preparation of rust structs and enums for deserialization of:

  • <text:p> - can have a children or can have a text or none of it
  • <text:span> - can have a children <office:annotation>, <office:annotation-end>, <text:s/> and text

I tried to implement it like this (ignoring attributes):

#[derive(Debug, XmlSerialize, XmlDeserialize)]
pub struct OfficeText {
    #[xmlserde(name = b"text:p", ty = "child")]
    pub text_p: TextP,
}

#[derive(Debug, XmlSerialize, XmlDeserialize)]
pub struct TextP {
    #[xmlserde(ty = "untag")]
    pub text_p_content: Option<TextPContent>,
}

#[derive(Debug, XmlSerialize, XmlDeserialize)]
pub enum TextPContent {
    #[xmlserde(ty = "text")]
    Text(String),
    #[xmlserde(name = b"text:span")]
    TextSpans(Vec<TextSpan>)
}

#[derive(Debug, XmlSerialize, XmlDeserialize)]
pub struct TextSpan {
    #[xmlserde(ty = "untag")]
    pub text_span_content: Vec<TextSpanContent>,
}

#[derive(Debug, XmlSerialize, XmlDeserialize)]
pub enum TextSpanContent {
    #[xmlserde(ty = "text")]
    Text(String),
    #[xmlserde(name = b"text:s", ty = "sfc")]
    TextS,
    #[xmlserde(name = b"office:annotation")]
    OfficeAnnotation(OfficeAnnotation),
    #[xmlserde(name = b"office:annotation-end", ty = "sfc")]
    OfficeAnnotationEnd(OfficeAnnotationEnd),
}

#[derive(Debug, XmlSerialize, XmlDeserialize)]
pub struct OfficeAnnotation {
    // whatever
}

#[derive(Debug, XmlSerialize, XmlDeserialize)]
pub struct OfficeAnnotationEnd {
    // whatever
}

Unfortunately, this is not compilable, but I wonder if there is a way, how to make it work with xmlserde crate.
Sorry if I made some silly mistake in code, I am new to Rust.

There might be 3 options how to make it work:

  1. I could try to make custom implementation of XmlSerialize and XmlDeserialize traits for these situations
  2. You might consider adding support for these situations to xmlserde crate
  3. You might consider to make members of "Unparsed" struct public, so it would be possible to do manual parsing/deserializing for these tricky parts

I would like to know your opinion, thanks.

Support generic structs / enums in derive

Currently this code panics with the following error:

use xmlserde_derives::XmlDeserialize;

#[derive(XmlDeserialize)]
struct Root<E> {
    item: E,
}
> cargo bench
   Compiling compare v0.1.0 (H:\Projects\quick-xml\compare)
error: proc-macro derive panicked
   --> benches\bench.rs:296:29
    |
296 |             #[derive(Debug, XmlDeserialize)]
    |                             ^^^^^^^^^^^^^^
    |
    = help: message: called `Option::unwrap()` on a `None` value

error[E0107]: missing generics for struct `Root`
   --> benches\bench.rs:237:12
    |
237 |     struct Root<E> {
    |            ^^^^ expected 1 generic argument
    |
note: struct defined here, with 1 generic parameter: `E`
   --> benches\bench.rs:237:12
    |
237 |     struct Root<E> {
    |            ^^^^ -
help: add missing generic argument
    |
237 |     struct Root<E><E> {
    |                +++

Simple example fails with proc-macro derive panicked

I'm trying to use this library but all I'm getting is arcane errors. Here is a simple reproducer:

use xmlserde::xml_deserialize_from_str;
use xmlserde_derives::XmlDeserialize;

#[derive(XmlDeserialize)]
#[xmlserde(root = b"foo")]
struct Foo {
    #[xmlserde(ty = "attr")]
    pub bar: String,
}

fn main() {
    println!(
        "{}",
        xml_deserialize_from_str::<Foo>(r#"<foo bar="baz" />"#)
            .unwrap()
            .bar
    );
}

And here's the output of cargo run:

   Compiling xmlserde_test v0.1.0 (/home/gabmus/xmlserde_test)
error: proc-macro derive panicked
 --> src/main.rs:4:10
  |
4 | #[derive(XmlDeserialize)]
  |          ^^^^^^^^^^^^^^
  |
  = help: message: called `Option::unwrap()` on a `None` value

error[E0277]: the trait bound `Foo: XmlDeserialize` is not satisfied
   --> src/main.rs:14:36
    |
14  |         xml_deserialize_from_str::<Foo>(r#"<foo bar="baz" />"#)
    |                                    ^^^ the trait `XmlDeserialize` is not implemented for `Foo`
    |
    = help: the trait `XmlDeserialize` is implemented for `Unparsed`
note: required by a bound in `xml_deserialize_from_str`
   --> /home/gabmus/.cargo/registry/src/index.crates.io-6f17d22bba15001f/xmlserde-0.7.1/src/lib.rs:376:8
    |
374 | pub fn xml_deserialize_from_str<T>(xml_str: &str) -> Result<T, String>
    |        ------------------------ required by a bound in this function
375 | where
376 |     T: XmlDeserialize,
    |        ^^^^^^^^^^^^^^ required by this bound in `xml_deserialize_from_str`

For more information about this error, try `rustc --explain E0277`.

How do I skip (de)serialize only?

Great crate, thank you very much.

We can skip only one of the (de)serialize to get the default value for serde.

This would be very useful.
https://serde.rs/attr-skip-serializing.html

If we specify a default, we don't want to be skipped.

In serde we have the following
https://serde.rs/attr-default.html

So my point is this.

  • Actual

    xmlserde/tests/lib.rs

    Lines 301 to 320 in 5dc1a8a

    fn serialize_skip_default() {
    fn default_age() -> u16 {
    12
    }
    #[derive(XmlSerialize)]
    #[xmlserde(root = b"Person")]
    struct Person {
    #[xmlserde(name = b"age", ty = "attr", default = "default_age")]
    age: u16,
    #[xmlserde(name = b"name", ty = "text")]
    name: String,
    }
    let p = Person {
    age: 12,
    name: String::from("Tom"),
    };
    let result = xml_serialize(p);
    assert_eq!(result, "<Person>Tom</Person>")
    }

  • Expected

    #[test]
    fn serialize_default() {
        fn default_age() -> u16 {
            12
        }
        #[derive(XmlSerialize)]
        #[xmlserde(root = b"Person")]
        struct Person {
            #[xmlserde(name = b"age", ty = "attr", default = "default_age")]
            age: u16,
            #[xmlserde(name = b"name", ty = "text")]
            name: String,
        }

        let p = Person {
            age: 12,
            name: String::from("Tom"),
        };
        let result = xml_serialize(p);
        assert_eq!(result, "<Person age=\"12\">Tom</Person>")
    }

    #[test]
    fn deserialize_default_with_skip_deserializing() {
        fn default_age() -> u16 {
            12
        }
        #[derive(XmlDeserialize)]
        #[xmlserde(root = b"Person")]
        struct Person {
            #[xmlserde(
                name = b"age",
                ty = "attr",
                default = "default_age",
                skip_deserializing
            )]
            age: u16,
            #[xmlserde(name = b"name", ty = "text")]
            name: String,
        }
        let xml = r#"<Person>Tom</Person>"#;
        let result = xml_deserialize_from_str::<Person>(xml);
        // `default` is specified, but the `skip_deserializing` specification **ignores the serialized xml value** and uses default.
        match result {
            Ok(p) => {
                assert_eq!(p.age, 12);
                assert_eq!(p.name, "Tom");
            }
            Err(_) => panic!(),
        }
    }
    #[test]
    fn serialize_default_with_skip_serializing() {
        fn default_age() -> u16 {
            12
        }
        #[derive(XmlSerialize)]
        #[xmlserde(root = b"Person")]
        struct Person {
            #[xmlserde(name = b"age", ty = "attr", default = "default_age", skip_serializing)]
            age: u16,
            #[xmlserde(name = b"name", ty = "text")]
            name: String,
        }

        let p = Person {
            age: 12,
            name: String::from("Tom"),
        };
        let result = xml_serialize(p);
        // default is specified, but the value is ignored in **serialize** by specifying `skip_serializing`.
        assert_eq!(result, "<Person>Tom</Person>")
    }

Unwanted removal of spaces at the beginning and end of the text

I see that reader.trim_text is set to false,
but still when I deserialize following xml:

<text:p text:style-name="P2">
    <text:span text:style-name="T1">Warning:</text:span>
    <text:span text:style-name="T3"> missing spaces at the </text:span>
    <text:span text:style-name="T2">beginning</text:span>
    <text:span text:style-name="T3"> and </text:span>
    <text:span text:style-name="T2">end</text:span>
    <text:span text:style-name="T3"> of this sentence </text:span>
</text:p>

and serialize it again, I get:

<text:p text:style-name="P2">
    <text:span text:style-name="T1">Warning:</text:span>
    <text:span text:style-name="T3">missing spaces at the</text:span>
    <text:span text:style-name="T2">beginning</text:span>
    <text:span text:style-name="T3">and</text:span>
    <text:span text:style-name="T2">end</text:span>
    <text:span text:style-name="T3">of this sentence</text:span>
</text:p>

Am I missing some setting? How can I fix this, please?
If you think there is a bug in my data structures I can share them.

Note: This happens only with documents, created by LibreOffice/OpenOffice. MS Word uses <text:s/> instead of leading/trailing whitespaces so there is no problem.

Support deny_unknown_fields like serde?

Currently, serde, provides a neat #[serde(deny_unknown_fields)] attribute that makes the parser fail if it encounters any field that is not properly handled in the defined struct/enum or so.

This allows to easily add conforms tests that all the possible tags are supported. This is very useful when writing a library to parse a specific xml format.

Issue getting basic example to compile

Hello! I'm trying to get the following example to compile:

use xmlserde_derives::XmlDeserialize;
use xmlserde_derives::XmlSerialize;

#[derive(XmlDeserialize)]
#[xmlserde(root = b"person")]
pub struct Person {
    #[xmlserde(name=b"age", ty="attr")]
    pub age: u8,
    #[xmlserde(ty ="text")]
    pub name: String,
}

fn deserialize_person() {
    use xmlserde::xml_deserialize_from_str;

    let s = r#"<person age ="16">Tom</person>"#;
    let p: Person = xml_deserialize_from_str(s).unwrap();
    assert_eq!(p.age, 16);
    assert_eq!(p.name, "Tom");
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    deserialize_person();

    Ok(())
}

I get the following compiler error:

error[E0053]: method `deserialize` has an incompatible type for trait
 --> src/main.rs:9:10
  |
9 | #[derive(XmlDeserialize)]
  |          ^^^^^^^^^^^^^^
  |          |
  |          expected struct `quick_xml::reader::Reader`, found struct `Reader`
  |          help: change the parameter type to match the trait: `&mut quick_xml::reader::Reader<B>`
  |
  = note: expected fn pointer `fn(&[u8], &mut quick_xml::reader::Reader<B>, quick_xml::events::attributes::Attributes<'_>, _) -> Person`
             found fn pointer `fn(&[u8], &mut Reader<B>, Attributes<'_>, _) -> Person`
  = note: this error originates in the derive macro `XmlDeserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0053`.
warning: `ieee20305-adapter` (bin "ieee20305-adapter") generated 2 warnings
error: could not compile `ieee20305-adapter` due to previous error; 2 warnings emitted

Fails to use a struct for an attribute

I am trying to parse an xml file that has various version attributes, so I want to create a wrapper struct pub struct Version(String); and use that instead of just an alias which would work fine.

Given the following xml

<repository xmlns="http://www.gtk.org/introspection/core/1.0" xmlns:c="http://www.gtk.org/introspection/c/1.0" xmlns:glib="http://www.gtk.org/introspection/glib/1.0" version="1.2">
</repository>

The following code should work

#[derive(Debug, XmlDeserialize)]
#[xmlserde(root = b"repository")]
pub struct Repository {
    #[xmlserde(name = b"version", ty = "attr")]
    version: Version,
}

#[derive(Debug, XmlDeserialize)]
pub struct Version {
    #[xmlserde(ty = "text")]
    inner: String,
}

Instead the code fails to compile with

error[E0061]: this function takes 4 arguments but 1 argument was supplied
   --> src/parser/repository.rs:20:17
    |
20  | #[derive(Debug, XmlDeserialize)]
    |                 ^^^^^^^^^^^^^^
    |                 |
    |                 expected `&[u8]`, found `&String`
    |                 three arguments of type `&mut Reader<_>`, `Attributes<'_>`, and `bool` are missing
    |
    = note: expected reference `&[u8]`
               found reference `&String`
note: associated function defined here
   --> /home/bilalelmoussaoui/.cargo/registry/src/index.crates.io-6f17d22bba15001f/xmlserde-0.7.0/src/lib.rs:183:8
    |
183 |     fn deserialize<B: BufRead>(
    |        ^^^^^^^^^^^
    = note: this error originates in the derive macro `XmlDeserialize` (in Nightly builds, run with -Z macro-backtrace for more info)
help: provide the arguments
    |
20  | #[derive(Debug, XmlDeserialize(/* &[u8] */, /* reader */, /* Attributes<'_> */, /* bool */))]
    |                               +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

error[E0308]: mismatched types
  --> src/parser/repository.rs:20:17
   |
20 | #[derive(Debug, XmlDeserialize)]
   |                 ^^^^^^^^^^^^^^
   |                 |
   |                 expected `Version`, found `Result<_, _>`
   |                 this expression has type `Version`
   |
   = note: expected struct `Version`
                found enum `Result<_, _>`
   = note: this error originates in the derive macro `XmlDeserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

Need to add quick xml to cargo toml in newest version

Whe I use the XmlDeserialize macro, I need to make sure to add the quick_xml crate to my cargo.toml. Specifically version 0.29, as this crate doesn't work with the newest version.

I think a way around this would be to re-export quick_xml from your crate, and then make the use statement in your derive code refer to that version. That way someone could use a different version of quick xml in their code than what your crate uses.

Alternatively, it would be good to document in the readme that quick_xml needs to be added to your dependencies in order to use this crate.

Thanks for the great work, this reads much better than serde!

Support self closed tags in enums

The current enum fails

#[derive(Debug, XmlDeserialize)]
pub enum ParameterType {
    #[xmlserde(name = b"type")]
    Type(Type),
    #[xmlserde(name = b"array")]
    Array(Array),
    // TODO: support self closing tags for untagged enums upstream
    #[xmlserde(name = b"varargs", ty = "sfc")]
    VarArgs,
}

Which would correspond to the following tag

<parameter-type>
<type>
</type>
</parameter-type>

<parameter-type>
<array>

</array>
</parameter-type>

<parameter-type>
<varargs />
</parameter-type>

I tried looking into it, but couldn't understand the de.rs code that much
this is what I have so far

diff --git a/derives/src/container.rs b/derives/src/container.rs
index 83f05f5..21c8aa0 100644
--- a/derives/src/container.rs
+++ b/derives/src/container.rs
@@ -222,13 +222,15 @@ impl<'a> StructField<'a> {
 
 pub struct EnumVariant<'a> {
     pub name: syn::LitByteStr,
+    pub element_type: Option<EleType>,
     pub ident: &'a syn::Ident,
-    pub ty: &'a syn::Type,
+    pub ty: Option<&'a syn::Type>,
 }
 
 impl<'a> EnumVariant<'a> {
     pub fn from_ast(v: &'a Variant) -> Self {
         let mut name = Option::<syn::LitByteStr>::None;
+        let mut element_type: Option<EleType> = Option::<EleType>::None;
         for meta_item in v
             .attrs
             .iter()
@@ -241,20 +243,31 @@ impl<'a> EnumVariant<'a> {
                         name = Some(s.clone());
                     }
                 }
+                NameValue(m) if m.path == TYPE => {
+                    if let Ok(s) = get_lit_str(&m.value) {
+                        let t = match s.value().as_str() {
+                            "sfc" => EleType::SelfClosedChild,
+                            _ => panic!("Only self closed child are supported as attributes"),
+                        };
+                        element_type = Some(t);
+                    }
+                }
                 _ => panic!("unexpected"),
             }
         }
-        let ty = &v.fields.iter().next().unwrap().ty;
+        let field = v.fields.iter().next();
         let ident = &v.ident;
         EnumVariant {
             name: name.unwrap(),
-            ty,
+            ty: field.map(|f| &f.ty),
             ident,
+            element_type,
         }
     }
 }
 
 /// Specify where this field is in the xml.
+#[derive(PartialEq, Eq)]
 pub enum EleType {
     Attr,
     Child,
diff --git a/derives/src/de.rs b/derives/src/de.rs
index 4b7440e..d35cd87 100644
--- a/derives/src/de.rs
+++ b/derives/src/de.rs
@@ -18,13 +18,14 @@ pub fn get_de_enum_impl_block(container: Container) -> proc_macro2::TokenStream
         let name = &v.name;
         let ty = v.ty;
         let ident = v.ident;
+        let is_self_closed = v.element_type == Some(EleType::SelfClosedChild);
         quote! {
             #name => {
                 let _r = #ty::deserialize(
                     #name,
                     reader,
                     _s.attributes(),
-                    false,
+                    #is_self_closed,
                 );
                 result = Some(Self::#ident(_r));
             }
@@ -34,13 +35,14 @@ pub fn get_de_enum_impl_block(container: Container) -> proc_macro2::TokenStream
         let name = &v.name;
         let ty = v.ty;
         let ident = v.ident;
+        let is_self_closed = v.element_type == Some(EleType::SelfClosedChild);
         quote! {
             #name => {
                 let _r = #ty::deserialize(
                     #name,
                     reader,
                     _s.attributes(),
-                    true,
+                    #is_self_closed,
                 );
                 result = Some(Self::#ident(_r));
             }
diff --git a/tests/lib.rs b/tests/lib.rs
index e058923..7a4b019 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -72,6 +72,25 @@ mod tests {
         assert_eq!(result.properties.0[0].name, "test",);
     }
 
+    #[test]
+    fn self_closed_child_untagged_enum() {
+        #[derive(Debug, XmlDeserialize)]
+        struct Type {
+            #[xmlserde(name = b"name", ty = "attr")]
+            name: String,
+        }
+
+        #[derive(Debug, XmlDeserialize)]
+        enum ParameterType {
+            #[xmlserde(name = b"type")]
+            Type(Type),
+            #[xmlserde(name = b"array")]
+            Array(Type),
+            #[xmlserde(name = b"varargs", ty = "sfc")]
+            VarArgs,
+        }
+    }
+
     #[test]
     fn self_closed_boolean_child() {
         #[derive(XmlDeserialize, Default)]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.