5
\$\begingroup\$

I need to build the following XML document

<?xml version="1.0"?>
<SFDocument>
    <TableID>5415</TableID>
    <PageSize>1</PageSize>
    <PageIndex>1</PageIndex>
    <MultiCompany>No</MultiCompany>
    <Language>1033</Language>
    <Fields>
        <Field>Operation No.</Field>
        <Field>Line No.</Field>
        <Field>Date</Field>
        <Field>Comment</Field>
    </Fields>
    <TableFilters>
        <TableFilter>
            <Field>Status</Field>
            <Filter>3</Filter>
        </TableFilter>
        <TableFilter>
            <Field>Prod. Order No.</Field>
            <Filter>101006</Filter>
        </TableFilter>
    </TableFilters>
    <Key>
        <Field>Routing Reference No</Field>
        <Field>10000</Field>
    </Key>
</SFDocument>

using the following code, it works but is it the proper way of doing it.

internal class Field
{
    public string Name { get; set; }
    public  const string FieldName = "Field";
}
internal class Key
{
    public string Field { get; set; }
    public  const string FieldName = "Field";
}

internal class TableFilter
{
    public string Field { get; set; }
    public string Filter { get; set; }
    public const string FieldName = "Field";
    public const string FilterName = "FilterName";
} 

void Main()
{
    //this part is just for the example, obviously, this would be passed as parameter
    var tableId = 5415;
    var pageSize = 1;
    var pageIndex = 1;
    var multiCompany = "No";
    var language = 1033;

    var fields = new List<Field>();
    fields.Add(new Field() {Name = "Operation No."});
    fields.Add(new Field() {Name = "Line No."});
    fields.Add(new Field() {Name = "Date"});
    fields.Add(new Field() {Name = "Comment"});

    var tableFilters = new List<TableFilter>();
    tableFilters.Add(new TableFilter() {Field = "Status", Filter = "3"});
    tableFilters.Add(new TableFilter() {Field = "Prod. Order No.", Filter = "101006"});

    var keys = new List<Key>();
    keys.Add(new Key() {Field = "Routing Reference No"});
    keys.Add(new Key() {Field = "10000"});

    //End of setup

    XDocument doc = new XDocument(new XElement("SFDocument",
                                      new XElement("TableId", tableId), 
                                      new XElement("PageSize", pageSize),
                                      new XElement("PageIndex", pageIndex),
                                      new XElement("MultiCompany", multiCompany),
                                      new XElement("Language", language),
                                      new XElement("Fields"),
                                        new XElement("TableFilters"),
                                        new XElement("Key")
                                        )
    );

    var xFields = doc.Element("SFDocument").Element("Fields");

    foreach (Field field in fields)
    {
        xFields.Add(new XElement(Field.FieldName, field.Name));
    }

    var xTableFilters = doc.Element("SFDocument").Element("TableFilters");

    foreach (TableFilter tableFilter in tableFilters)
    {
        var tf = new XElement("TableFilter");
        tf.Add(new XElement(TableFilter.FieldName, tableFilter.Field));
        tf.Add(new XElement(TableFilter.FilterName, tableFilter.Filter  ));
        xTableFilters.Add(tf);
    }

    var xKey = doc.Element("SFDocument").Element("Key");

    foreach (Key key in keys)
    {
        xKey.Add(new XElement(Key.FieldName, key.Field));
    }       
    doc.Dump();  //Using LinqPad
}
\$\endgroup\$
2
  • 2
    \$\begingroup\$ Recently i have started using serializable classes for these kinds of things, the advantage being that i can easily read the XML document back into the same structure, by deserializing it. And if you ever what the change the format of the XML, you don't have update your logic, just add the attribute the the serializable class, and the serializer will handle the rest. Just found this article over at code project: codeproject.com/Articles/483055/… that might be of help. \$\endgroup\$ – jolt Nov 27 '13 at 21:25
  • \$\begingroup\$ I tried and loved that way... I will post how i did it below \$\endgroup\$ – SerenityNow Nov 28 '13 at 19:58
4
\$\begingroup\$

I ended up changing my approach and went with a serialize method:

public static class SerializationHelper
{
    internal static string SerializeObject<T>(this T toSerialize)
    {
        var xmlSerializer = new XmlSerializer(toSerialize.GetType());
        var textWriter = new StringWriter();
        xmlSerializer.Serialize(textWriter, toSerialize);
        return textWriter.ToString();
    }
}
[Serializable]
public class SFDocument
{
    public int TableId { get; set; }
    public int PageSize { get; set; }
    public int PageIndex { get; set; }
    public string MultiCompany { get; set; }
    public int Language { get; set; }

        [XmlArrayItem("Field")]
        public List<string> Fields { get; set; }

        public List<TableFilter> TableFilters { get; set; }

        [XmlArrayItem("Field")]
        public List<string> Key { get; set; }
        public SFDocument(): this(5415, 1, 1, "No", 1033){}

        public SFDocument(int tableId, int pageSize, int pageIndex, string multiCompany, int language)
        {
            TableId = tableId;
            PageSize = pageSize;
            PageIndex = pageIndex;
            MultiCompany = multiCompany;
            Language = language;

            Fields = new List<string>();
            Key = new List<string>();
            TableFilters  = new List<TableFilter>();
        }
}   

[Serializable]
public class TableFilter
{
    public string Field { get; set; }
    public string Filter { get; set; }

    public TableFilter() {}

    public TableFilter(string field, string filter)
    {
        Field = field;
        Filter = filter;
    }
}

void Main()
{
    var sfd = new SFDocument();
    sfd.Fields.Add("Field1");
    sfd.Fields.Add("Field2");
    sfd.Fields.Add("Field3");
    sfd.Fields.Add("Field4");

    sfd.Key.Add("Key1");
    sfd.Key.Add("Key2");
    sfd.Key.Add("Key3");

    sfd.TableFilters.Add(new TableFilter("field1", "filter1"));
    sfd.TableFilters.Add(new TableFilter("field2", "filter2"));
    sfd.TableFilters.Add(new TableFilter("field3", "filter3"));
    sfd.SerializeObject().Dump();
}
\$\endgroup\$
1
  • \$\begingroup\$ Feel free to accept an answer, doing so will remove this question from the stack of unanswered zombies and help bring up the site's metrics (we're a beta site!) \$\endgroup\$ – Mathieu Guindon Nov 28 '13 at 20:52
3
\$\begingroup\$

Using XML serialization is certainly a valid approach. But if you wanted to keep using LINQ to XML, I would change the code to be more declarative. This way, the structure of the code would closely resemble the structure of the XML, so it would be clearer to see what's going on:

XDocument doc = new XDocument(
    new XElement(
        "SFDocument",
        new XElement("TableId", tableId),
        new XElement("PageSize", pageSize),
        new XElement("PageIndex", pageIndex),
        new XElement("MultiCompany", multiCompany),
        new XElement("Language", language),
        new XElement(
            "Fields", fields.Select(field => new XElement(Field.FieldName, field.Name))),
        new XElement(
            "TableFilters",
            tableFilters.Select(
                tableFilter =>
                new XElement(
                    "TableFilter",
                    new XElement(TableFilter.FieldName, tableFilter.Field),
                    new XElement(TableFilter.FilterName, tableFilter.Filter)))),
        new XElement("Key", keys.Select(key => new XElement(Key.FieldName, key.Field)))));

Though the TableFilters element is probably too complicated for this approach. But you can easily refactor that to another method:

private static XElement CreateTableFilters(IEnumerable<TableFilter> tableFilters)
{
    return new XElement(
        "TableFilters",
        tableFilters.Select(
            tableFilter =>
            new XElement(
                "TableFilter",
                new XElement(TableFilter.FieldName, tableFilter.Field),
                new XElement(TableFilter.FilterName, tableFilter.Filter))));
}

…

XDocument doc = new XDocument(
    new XElement(
        "SFDocument",
        new XElement("TableId", tableId),
        new XElement("PageSize", pageSize),
        new XElement("PageIndex", pageIndex),
        new XElement("MultiCompany", multiCompany),
        new XElement("Language", language),
        new XElement(
            "Fields", fields.Select(field => new XElement(Field.FieldName, field.Name))),
        CreateTableFilters(tableFilters),
        new XElement("Key", keys.Select(key => new XElement(Key.FieldName, key.Field)))));
\$\endgroup\$
1
  • \$\begingroup\$ I like how you made it much more readable. However, I didn't want to necessarily stick to Linq to XML. \$\endgroup\$ – SerenityNow Nov 29 '13 at 14:06
1
\$\begingroup\$

Back when using .NET 2.0, I wrote a Tag class which inherited from XmlElement, and a XmlBuilder which inherited from XmlDocument. For legacy reasons we still use it, but I'm pretty sure it could be rewritten in half the code as a few extension methods.

Here's a small sample:

XmlBuilder xml = new XmlBuilder("AmazonEnvelope");
xml.AddNamespace("noNamespaceSchemaLocation", "amzn-envelope.xsd");
XmlBuilder.Tag message = xml.FirstTag.AddTag("Message");
message.AddTag("MessageID", messageID++);
message.AddTag("OperationType", "PartialUpdate");

XmlBuilder.Tag product = message.AddTag("Product");
product.AddTag("SKU", part.ID);

if (part.upcCode.Length == 12)
{
    XmlBuilder.Tag UPC = product.AddTag("StandardProductID");
    UPC.AddTag("Type", "UPC");
    UPC.AddTag("Value", part.upcCode);
}
else if (part.upcCode.Length == 13)
{
    XmlBuilder.Tag UPC = product.AddTag("StandardProductID");
    UPC.AddTag("Type", "EAN");
    UPC.AddTag("Value", part.upcCode);
}

product.AddTag("ProductTaxCode", "A_GEN_TAX");
return xml.OuterXml

The XmlBuilder constructor just takes the string and uses that to create the first node:

_outerNode = new Tag(qualifiedName, this);
AppendChild(_outerNode);

xml.FirstTag is just a call to get _outerNode as a Tag (instead of an XmlElement).

And so on...

This lets you easily add a lot of logic ("Do I add this set of tags?"), doesn't require a multitude of news, and you can go back and add more things to a tag later.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.