How to implement mutually exclusive struct fields

Hello everyone,
Please I have a data model (called GeoLocation) with mutually exclusive fields (i.e point and region. Only one field must be present in the struct when it is returned) that I need to convert to JSON using serde. How do I implement this data structure in the most efficient way?

The data model for GeoLocation:

GeoLocation
point:Ellipse
region:Polygon
confidence:int // optional field

Note: point and region are mutually exclusive. Exactly one must be present.

My idea

struct Ellipse {}
struct Polygon {}

struct GeoLocation {
  union mutExField{                    // How do I make the Union nameless?
    point: Ellipse,          // Mutually exclusive
    region: Polygon,         // Mutually exclusive
  }
    confidence: Option<u32>, // OPTIONAL field
}

impl GeoLocation {
   fn to_json() -> String {
      let geo = GeoLocation {
        point: Ellipse::new(),  // I don't want the Union name to appear in the JSON string
        confidence: None
      };
      let s = serde_json::to_string_pretty(&geo).unwrap();
      return s;
   }
}

Thanks.

I am not sure how it looks in JSON, but what about enums?

Loc enum {
      point(Ellipse),
      region(Polygon)
}
GeoLocation struct {
      loc: Loc,
      confidence: int,
}

something like this

1 Like

Thanks Vladmir. The issue is with the loc field. How do I get rid of it in the to_json method of GeoLocation similar to my union mutExField

The expected JSON output

"GeoLocation": {
      "point": {
       "Ellipse data..."
      }
     }

From what I see in docs (Enum representations · Serde),

enum Loc {
   point(Ellipse),
   region(Polygon)
}

its JSON should be "point": { Ellipse fields ... } in case of GeoLocation.Loc = point(,,,)

1 Like
#[derive(Debug, Deserialize, Serialize, PartialEq)]
#[serde(untagged)]
enum Shape {
    Ellipse { i: i32 },
    Polygon { a: i32 },
}

#[derive(Debug, Deserialize, Serialize, PartialEq)]
struct GeoLocation {
    #[serde(flatten)]
    point: Shape,
    confidence: Option<u32>,
}

Do you want something like that ? playground

3 Likes

Thanks for the hint. I have been able to fix it.

Here it is:

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
enum Location {
    Point(Ellipse),
    Region(Polygon),
}

#[derive(Serialize, Deserialize)]
struct GeoLocation {
    #[serde(flatten)]
    loc: Location,
    #[serde(skip_serializing_if = "Option::is_none")]
    confidence: Option<u32>,
}

playground

Result:

 "location": {
    "point": {
      "center": {
        "latitude": 00.0,
        "longitude": 00.0
      }
    }
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.