0

I read a sales order from an XML file and send a DTO to a restful API where the customer is a string. The returned DTO contains a customer object, and therefore does not match the string in the input object.

Is there a way to create Customer object so that both the customer string as DTO and the returned customer object works?

Basically I want to read the string customer into the customer object and make the API think that it is just a customer string and not the customer object.

XML file:

<SalesOrder>
  <orderType>SO</orderType>
  <customer>10000</customer>
</SalesOrder>

Customer Object returned from API:

 <customer>
      <number>10000</number>
      <internalId>2462</internalId>
      <lastModifiedDateTime>0001-01-01</lastModifiedDateTime>
     </customer>

The customer get and set methods in SalesOrder

[XmlElement("customer")]
public Customer customer
{
    get { return Get("customer", new Customer()); }
    set { Set(value); }
}
dbc
  • 104,963
  • 20
  • 228
  • 340
Sindresvends
  • 180
  • 1
  • 1
  • 10
  • have you thought about serializing the object & then deserializing it? – Sid Nov 03 '16 at 08:52
  • Yes, I guess my problem is deserializing the customer string into the customer object. It is just null. – Sindresvends Nov 03 '16 at 08:55
  • 1
    It's not clear what you're asking. How do these three snippets relate to each other? What are you deserializing, how are you doing it, what is null, and what do you expect? A [mcve] is required. – Charles Mager Nov 03 '16 at 09:51

2 Answers2

0

Consider using Newtonsoft.Json for serializing & deserializing

Serialization example:

var customer = new Customer();
var serialized = Newtonsoft.Json.JsonConvert.SerializeObject(Customer); 

Deserialization @ Server Side

var customer = Newtonsoft.Json.JsonConvert.DeserializeObject(serializedObject);

Where serializedObject is a string got from the request.

Sid
  • 14,176
  • 7
  • 40
  • 48
0

To clarify your question, you want to be able to design your Customer DTO such that it can be used to deserialize the following simple XML:

<SalesOrder>
  <orderType>SO</orderType>
  <customer>10000</customer>
</SalesOrder>

As well as the following more complex XML:

<SalesOrder>
  <orderType>SO</orderType>
  <customer>
      <number>10000</number>
      <internalId>2462</internalId>
      <lastModifiedDateTime>0001-01-01</lastModifiedDateTime>
  </customer>
</SalesOrder>

However, the following XML would be invalid and attempting to deserialize it should throw an exception, since it has a both a simple customer number and a complex set of customer values:

<SalesOrder>
  <orderType>SO</orderType>
  <customer>10000<number>10000</number>
      <internalId>2462</internalId>
      <lastModifiedDateTime>0001-01-01</lastModifiedDateTime>
  </customer>
</SalesOrder>

You also want to be able to choose whether the customer should be re-serialized in simple or complex format.

This can be done with XmlSerializer, but there is no built-in support for an element type that might have a text value, or might have a sequence of child elements -- but not both. Thus you will need to support this logic in the class itself. The following does this:

[XmlRoot(ElementName = "customer")]
public class Customer
{
    bool? isSimpleFormat;

    [XmlIgnore]
    public bool IsSimpleFormat { get { return isSimpleFormat == true; } set { isSimpleFormat = value; } }

    [XmlIgnore]
    bool IsSimpleFormatDefined { get { return isSimpleFormat != null; } }

    [XmlIgnore]
    bool IsComplexPropertySpecified
    {
        get
        {
            return !IsSimpleFormat;
        }
        set
        {
            if (IsSimpleFormatDefined && IsSimpleFormat)
            {
                throw new InvalidOperationException("Cannot set a complex property on a simple customer");
            }
            IsSimpleFormat = !value;
        }
    }

    [XmlElement(ElementName = "number")]
    public string Number { get; set; }

    [XmlIgnore]
    public bool NumberSpecified { get { return IsComplexPropertySpecified; } set { IsComplexPropertySpecified = value; } }

    [XmlElement(ElementName = "internalId")]
    public string InternalId { get; set; }

    [XmlIgnore]
    public bool InternalIdSpecified { get { return IsComplexPropertySpecified; } set { IsComplexPropertySpecified = value; } }

    [XmlElement(ElementName = "lastModifiedDateTime")]
    public string LastModifiedDateTime { get; set; }

    [XmlIgnore]
    public bool LastModifiedDateTimeSpecified { get { return IsComplexPropertySpecified; } set { IsComplexPropertySpecified = value; } }

    [XmlText]
    public string SimpleNumber
    {
        get 
        {
            return IsSimpleFormat ? Number : null; 
        }
        set
        {
            IsComplexPropertySpecified = false;
            Number = value;
        }
    }
}

[XmlRoot(ElementName = "SalesOrder")]
public class SalesOrder
{
    [XmlElement(ElementName = "orderType")]
    public string OrderType { get; set; }
    [XmlElement(ElementName = "customer")]
    public Customer Customer { get; set; }
}

Some notes:

  1. When initially constructed, a Customer defaults to being in complex format, but will adapt depending whether a child element or text value is encountered during deserialization. Once the format is set, it will no longer adapt and an exception will be thrown if additional nodes are encountered in the wrong format.

  2. At any time after construction, your code can change whether a Customer is to be serialized in simple or complex format by setting the Boolean property Customer.IsSimpleFormat.

  3. The type takes advantage of the *Specified pattern of XmlSerializer to track and control whether or not the child properties of Customer were deserialized, and should be serialized.

  4. The XML text value of an element that corresponds to a complex POCO can be mapped to a property by marking that property of that POCO with [XmlText].

Sample fiddle.

dbc
  • 104,963
  • 20
  • 228
  • 340