Serialized WCF array or collection without container

I have a situation where we are connecting to a SOAP service.

The answer we get looks something like this:

<SomeObject>
    <item1>1</item1>
    <thing1>2</thing1>
    <arrayItem><foo>text</foo></arrayItem>
    <arrayItem><foo>text1</foo></arrayItem>
    <arrayItem><foo>text2</foo></arrayItem>
</SomeObject>

      

I need to replicate the result of this answer. The problem I keep working with is what is <arrayItem>

encapsulated <arrayItemList>

and I really need to <arrayItemList>

go away.

Does anyone know what I can overlay on my WCF objects to properly serialize / deserialize the objects we receive?

EDIT

The object I'm working with looks something like this:

[DataContract]
public class SomeObject
{
    [DataMember(Order = 0)]
    public string item1 {get;set;}

    [DataMember(Order = 1)]
    public string thing1 {get;set;}

    [DataMember(Order = 2)]
    public List<arrayItem> {get;set;}
}

[DataContract]
public class arrayItem
{
    [DataMember]
    public string foo {get;set;}
}

      

+3


source to share


2 answers


Unfortunately, I haven't been able to find a great solution. However, I found a suitable solution.

Warning. If at all possible, you should try to change the WSDL to prevent the need for this solution. This is more of a hack and a suggested solution, but will work as a last resort.

The solution was to implement IClientMessageInspector and IEndpointBehavior. These interfaces allow you to access the raw text request and response. They allowed messages to be modified before they were sent to the service or deserialized by WCF. Below is my implementation and a custom class that allowed the modification I needed to post.



public class MyService : IClientMessageInspector
{
    public void DoWork()
    {
         // Do some stuff
    }

    public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
    {
        string message = reply.ToString();

        // Load the reply message in DOM for easier modification
        XmlDocument doc = new XmlDocument();
        doc.Load(reply.GetReaderAtBodyContents());

        // Perform the modification
        MessageHelper.FixArrays(doc);

        // Create New Message
        XmlNodeReader reader = new XmlNodeReader(doc.DocumentElement);
        Message newMsg = Message.CreateMessage(reply.Version, reply.Headers.Action, reader);

        // Preserve the headers of the original message
        if (reply.Headers.Any())
            newMsg.Headers.CopyHeaderFrom(reply, 0);

        foreach (string propertyKey in reply.Properties.Keys)
        {
            newMsg.Properties.Add(propertyKey, reply.Properties[propertyKey]);
        }

        // Close the original message and return new message
        reply.Close();
        reply = newMsg;
    }

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
    {
        return null;
    }
}

public static class MessageHelper
{
    public static void FixArrays(XmlDocument doc)
    {
        // Arrearage
        WrapElement(doc, "foo", "arrayItem", "http://url.com/namespace/foo");
    }

    private static void WrapElement(XmlDocument doc, string elementName, string wrapperName, string nameSpace)
    {
        var names = new XmlNamespaceManager(doc.NameTable);
        names.AddNamespace("a", nameSpace);

        var Nodes = doc.SelectNodes("//a:" + elementName, names);

        if (Nodes.Count > 0)
        {
            var newBorrower = doc.CreateElement(Nodes.Item(0).Prefix, wrapperName, Nodes.Item(0).NamespaceURI);

            foreach (XmlElement node in Nodes)
            {
                newBorrower.AppendChild(node.Clone());
            }

            Nodes.Item(0).ParentNode.ReplaceChild(newBorrower, Nodes.Item(0));

            for (int i = 1; i <= Nodes.Count - 1; i++)
            {
                Nodes.Item(i).ParentNode.RemoveChild(Nodes.Item(i));
            }
        }
    }

    private static void WrapRenameElement(XmlDocument doc, string newName, string elementName, string wrapperName, string nameSpace, string newNamespace)
    {
        var names = new XmlNamespaceManager(doc.NameTable);
        names.AddNamespace("a", nameSpace);
        names.AddNamespace("b", newNamespace);

        var Nodes = doc.SelectNodes("//a:" + elementName + "/..", names);

        foreach (XmlElement parent in Nodes)
        {
            var newBorrower = doc.CreateElement(parent.Prefix, wrapperName, parent.NamespaceURI);

            foreach (XmlElement child in parent.ChildNodes)
            {
                if (child.LocalName == elementName)
                {
                    var newNode = RenameNode(child.Clone(), newNamespace, newName);
                    parent.RemoveChild(child);
                    newBorrower.AppendChild(newNode);
                }
            }

            if (newBorrower.ChildNodes.Count > 0)
                parent.AppendChild(newBorrower);
        }
    }

    private static void WrapRenameElement(XmlDocument doc, string newName, string elementName, string wrapperName, string nameSpace)
    {
        var names = new XmlNamespaceManager(doc.NameTable);
        names.AddNamespace("a", nameSpace);

        var Nodes = doc.SelectNodes("//a:" + elementName + "/..", names);

        foreach (XmlElement parent in Nodes)
        {
            var newBorrower = doc.CreateElement(parent.Prefix, wrapperName, parent.NamespaceURI);

            foreach (XmlElement child in parent.ChildNodes)
            {
                if (child.LocalName == elementName)
                {
                    var newNode = RenameNode(child.Clone(), nameSpace, newName);
                    parent.RemoveChild(child);
                    newBorrower.AppendChild(newNode);
                }
            }

            if (newBorrower.ChildNodes.Count > 0)
                parent.AppendChild(newBorrower);
        }
    }

    public static XmlNode RenameNode(XmlNode node, string namespaceURI, string qualifiedName)
    {
        if (node.NodeType == XmlNodeType.Element)
        {
            XmlElement oldElement = (XmlElement)node;
            XmlElement newElement =
            node.OwnerDocument.CreateElement(qualifiedName, namespaceURI);

            while (oldElement.HasAttributes)
            {
                newElement.SetAttributeNode(oldElement.RemoveAttributeNode(oldElement.Attributes[0]));
            }

            while (oldElement.HasChildNodes)
            {
                newElement.AppendChild(oldElement.FirstChild);
            }

            if (oldElement.ParentNode != null)
            {
                oldElement.ParentNode.ReplaceChild(newElement, oldElement);
            }

            return newElement;
        }
        else
        {
            return null;
        }
    }
}

      

As I said, this is ugly and essentially a hack, but this solution will work for my problem. I hope no one else should be able to handle this, but if they do, I hope this helps.

+4


source


If I get what you are looking for, try adding the attribute:

[XmlElement("arrayItem")]
public List<arrayItem> arrayItems {get; set;}

      

EDIT:

ok I quickly tried an example and this is what worked for me:

Objects:



 [DataContract]
 public class SomeObject
 {
     [DataMember(Order = 0)]
     [XmlElement()]
     public string item1 { get; set; }

     [DataMember(Order = 1)]
     [XmlElement()]
     public string thing1 { get; set; }

     [DataMember(Order = 2)]
     [XmlElement("arrayItem")]
     public List<arrayItem> arrayItems { get; set; }

     public SomeObject()
     {
         arrayItems = new List<arrayItem>();
     }
 }

 [DataContract]
 public class arrayItem
 {
     [DataMember]
     [XmlElement()]
     public string foo { get; set; }
 }

      

Used code:

XmlSerializerNamespaces _namespaces = new XmlSerializerNamespaces();
_namespaces.Add(string.Empty, string.Empty);
SomeObject sm = new SomeObject();
sm.arrayItems.Add(new arrayItem() { foo = "foo1" });
sm.arrayItems.Add(new arrayItem() { foo = "foo2" });
sm.item1 = "item1";
sm.thing1 = "thing1";
_xmlSerializer = new XmlSerializer(typeof(SomeObject));
//writer is XmlWriter which writes data to response stream
_xmlSerializer.Serialize(writer, sm, _namespaces);

      

Result:

<SomeObject>
  <item1>item1</item1>
  <thing1>thing1</thing1>
  <arrayItem>
    <foo>foo1</foo>
  </arrayItem>
  <arrayItem>
    <foo>foo2</foo>
  </arrayItem>
</SomeObject>

      

+1


source







All Articles