Changing class name on serialization with [DataContract(Name = "Poco")]

I’m trying to figure out a way to separate Custom fields from my Poco base implementation while contacting the Pipedrive API.

It’s probably not the right way to do this but this is what I tried:

[DataContract(Name = “Poco”)]
public class PocoSandbox : PocoBase
{
}

[DataContract(Name = “Poco”)]
public class PocoProduction : PocoBase
{
}

[DataContract(Name = “Poco”)]
public class PocoBase
{
}

I’m naively expecting [DataContract(Name = “Poco”)] to act like [DataMember(Name = “”)]

I also tried

[DataContract(Name = “Poco”)]
public class PocoSandbox : PocoBase
{
}

[DataContract(Name = “Poco”)]
public class PocoProduction : PocoBase
{
}

public class PocoBase
{
}

In both case the properties in PocoBase are not deserialize as I would expect.

Is this supported? And if so what’s the proper way to accomplish this…?

I removed [DataContract(Name = “Poco”)] everywhere and it works.

I didn’t think to try that…

I’m curious to see how people implement the support for custom fields when accessing external api…

If you have an example somewhere let me know. Otherwise you can ignore this question.

If you add a [DataContract] it changes the serializtion to opt-in and you’d then need to add [DataMember] attribute on all properties you want serialized.

Note: JSON serialization is schema-less and doesn’t embed the name of the Type when serializing so [DataContract(Name = "Poco")] wont change the name as JSON doesn’t emit Type names, it’s only for serialization formats that do like XML.

I’m not clear on what you mean by “implement the support for custom fields”, it would help if you could provide the desired wire format containing the custom fields you’re trying to get to.

Super. Thanks for the quick reply. I didn’t know about the opt-in.

I’m working on a Pipedrive client. They support custom fields that the user can create (a hash key is used for the name) and that are specific to their own account.

I’m trying to keep the implementation details specific to our business (aka the custom fields we added) outside the client (if possible).

Here is a specific example targeting one of their “object” the PipedriveDeal.

So in my client I created a PipedriveDeal dto (with none of our custom fields) and 2 others dto classes PipedriveDealProduction and PipedriveDealSandboxing that inherit from PipedriveDeal.

PipedriveDealProduction and PipedriveDealSandboxing have our custom fields (their hash key is different in production and on the sandboxing account. Here is an example of one of these field:

public class PipedriveDealSandbox : PipedriveDeal
{
      [DataMember(Name = PipedriveDealCustomFields.PropositionNumber.ProductionKey)]
      public string PropositionNumber { get; set; }
 }

 public class PipedriveDealProduction : PipedriveDeal
 {
      [DataMember(Name = PipedriveDealCustomFields.PropositionNumber.SandboxingKey)]
     public string PropositionNumber { get; set; }
  }

A side effect of this is that I need to maintain:

public class CreatePipedriveDealNoCustomFields : IPost, IReturn<<PipedriveCollection<PipedriveDeal>>

public class CreatePipedriveDealWithProductionCustomFields : IPost, IReturn<PipedriveCollection<PipedriveDealProduction>>

public class CreatePipedriveDealWithSandboxingCustomFields : IPost, IReturn<PipedriveCollection<PipedriveDealSandbox>>

Same for:

public class DeletePipedriveDeal : IDelete, IReturn<PipedriveCollection<PipedriveDeal>>

So I’m looking for ideas on how to accomplish this.

Why can’t you flatten it so all properties are in PipedriveDeal? Also not sure why you’re using the same PropositionNumber property name but different Aliases? The DTOs should be designed around the 3rd Party API they’re modeled after so the only properties I’d have are Pipeline API properties (i.e. no aliases required). Looks like you’re trying to mix your App logic into Pipedrive specific DTOs which I wouldn’t do, the Service Gateway should have a clear role of just being a generic Typed gateway around the Pipedrive API i.e. without being influenced by the App that’s using it.

Designing for a 3rd party API should be a simple exercise of creating DTOs to map 1:1 with the wire format that your 3rd Party API accepts. If you look at the Request DTOs and DTO Types in Stripe Gateway they’re designed to close match the Types in Stripe’s REST API.

Personally I’d be using inheritance sparingly in DTOs (if at all) as properties decoratively describe the API Service Contract (i.e. not try to hide them behind inheritance), it looks like your goal of hiding them in inherited classes appears to be the source of friction.

It’s because PropositionNumber is a custom field we created in both our Pipedrive accounts: production and sandboxing.

Any Pipedrive customer can log in their account and create new fields specific to their needs. PropositionNumber is such a field that we created in both our account: production and sandboxing.

Once these fields are created, the Pipedrive API will return them with any Get request as if they were natively part of the Pipedrive API…

Of course these new fields are do not have the same hash key because they are in different account hence the different aliases:

public static class PropositionNumber
{
    public const string SandboxingKey = "f8b563ef466235fc8f62273a02d9214b1ef78b5c";
    
    public const string ProductionKey ="db873dfa2fb86fd55ab1c4fxe6f5b1eaa8327091";
}

I would prefer not to use inheritance at all.

Also, if someone else want to use the client with another Pipedrive account also potentially containing their own custom fields this quickly become unwieldy… One solution would be to have them inherit from default PipedriveDeal and implement their own derived Type (again inheritance not great).

I can’t really flatten everything in PipedriveDeal since I would bascially end up with:

public class PipedriveDeal
{
      [DataMember(Name = PipedriveDealCustomFields.PropositionNumber.ProductionKey)]
      public string PropositionNumber { get; set; }

      [DataMember(Name = PipedriveDealCustomFields.PropositionNumber.SandboxingKey)]
     public string PropositionNumber { get; set; }
}

I don’t think Stripe has any custom field that a customer can create and become part of their API.

It’s an interesting problem since the 1:1 mapping can actually change from one Pipedrive account to another…

I hope this is clearer…?

ok yeah I didn’t know Pipedrive had their own concept of “Custom Field” as I’m not familiar with Pipedrive before (probably no-one else here has either).

Yeah Stripe has a Metadata Type which lets customers store additional metadata against many different types. It seems like it was a poor API design choice by Pipedrive if they’ve forced you to use serialized guids in first-class properties, which results in being poorly readable on the wire whilst being hard for consumers to interoperate with, if it was just under a JSON object you could’ve used a loose-typed data structure like a generic String dictionary.