quick-xml icon indicating copy to clipboard operation
quick-xml copied to clipboard

Easy element construction with body

Open patrickelectric opened this issue 5 years ago • 4 comments

My objective is to create a XML element that has the following format:

<?xml version="1.0" encoding="UTF-8"?>
<Camera>
   <definition version="42">
      <model>Potato</model>
      <vendor>PotatoFarm</vendor>
   </definition>
</Camera>

But to accomplish that, from what I read from the examples, is necessary to create a structure like this one:

#[derive(Debug, Default, Serialize)]
struct Camera {
    definition: Definition,
}

#[derive(Debug, Default, Serialize)]
struct Definition {
    version: u32,
    model: Model,
    vendor: Vendor,
}

#[derive(Debug, Default, Serialize)]
struct Model {
    #[serde(rename = "$value")]
    body: String,
}

#[derive(Debug, Default, Serialize)]
struct Vendor {
    #[serde(rename = "$value")]
    body: String,
}

Is these struct with body element really necessary or is there a way to accomplish it with a macro or something to a simple struct like:

#[derive(Debug, Default, Serialize)]
struct Camera {
    definition: Definition,
}

#[derive(Debug, Default, Serialize)]
struct Definition {
    #[magic_macro]
    camera_info: CameraInfo,
}

#[derive(Debug, Default, Serialize)]
struct CameraInfo {
    vendor: String,
    model: String,
}

patrickelectric avatar Feb 15 '21 15:02 patrickelectric

It can be done with:

struct Definition {
    version: u32,
    #[serde(flatten)]
    camera_info: CameraInfo,
}

patrickelectric avatar Feb 15 '21 17:02 patrickelectric

It can be done with:

struct Definition {
    version: u32,
    #[serde(flatten)]
    camera_info: CameraInfo,
}

This will result in:

<?xml version="1.0" encoding="UTF-8"?>
<Camera>
   <definition>
      <version>32</version>
      <model>Potato</model>
      <vendor>PotatoFarm</vendor>
   </definition>
</Camera>

patrickelectric avatar Feb 15 '21 17:02 patrickelectric

I've found using serde very troublesome for serialization. XML just doesn't fit into the serde model very well

The manual (non-serde) way to do this would be:

// <?xml version="1.0" encoding="UTF-8"?>
writer.write_event(Event::Decl(BytesDecl::new(b"1.0", Some(b"UTF-8"), None)))?;

// <Camera>
let camera_tag = BytesStart::borrowed_name(b"Camera");
writer.write_event(Event::Start(camera_tag.to_borrowed()))?;

// <definition version="42">
let mut definition_tag = BytesStart::borrowed_name(b"definition");
definition_tag.push_attribute(("version", "42"));
writer.write_event(Event::Start(definition_tag.to_borrowed()))?;

// <model>Potato</model>
let model_tag = BytesStart::borrowed_name(b"model");
writer.write_event(Event::Start(model_tag.to_borrowed()))?;
writer.write_event(Event::Text(BytesText::from_plain_str("Potato")))?;
writer.write_event(Event::End(model_tag.to_end())?;

// <vendor>PotatoFarm</vendor>
let vendor_tag = BytesStart::borrowed_name(b"vendor");
writer.write_event(Event::Start(vendor_tag.to_borrowed()))?;
writer.write_event(Event::Text(BytesText::from_plain_str("PotatoFarm")))?;
writer.write_event(Event::End(vendor_tag.to_end())?;

// </definition>
writer.write_event(Event::End(definition_tag.to_end())?;

// </Camera>
writer.write_event(Event::End(camera_tag.to_end())?;

Once this PR merges (and is released) it will be simpler:

// <?xml version="1.0" encoding="UTF-8"?>
writer.write_event(Event::Decl(BytesDecl::new(b"1.0", Some(b"UTF-8"), None)))?;

// <Camera>
let camera_tag = BytesStart::borrowed_name(b"Camera");
writer.write_event(Event::Start(camera_tag.to_borrowed()))?;

// you could write the camera tags like this also but I don't like using more than one or two levels of nesting w/ this style.
// <definition version="42">
writer
    .create_element("definition")
    .with_attribute(("version", "42"))
    .write_inner_content(|writer| {
         // <model>Potato</model>
         writer.create_element("model").write_text_content(BytesText::from_plain_str("Potato"))?;

         // <vendor>PotatoFarm</vendor>
         writer.create_element("vendor").write_text_content(BytesText::from_plain_str("PotatoFarm"))?;

         Ok(())
        })?;
// </definition>

// </Camera>
writer.write_event(Event::End(camera_tag.to_end())?;

dralley avatar Mar 30 '21 02:03 dralley

Somewhat related, and depending on current progress: Would it be possible to add something similar to the manual write example above to the README as a simple intro for how to write XML from scratch? If yes, please include setting the initial values for the Writer/root + declaration. It's often difficult to find the starting point. The current writer example shows editing existing XML, but not how to build from scratch. It would also help with finding answers in the code itself.

ghost avatar Oct 30 '21 09:10 ghost