список элементов xml в массив общего базового типа - PullRequest
1 голос
/ 11 апреля 2011

У меня есть XML-файл со списком, подобным этому:

<TestFile>
  <string>Foo</string>
  <bool>false</bool>
  <bool>true</bool>
  <string>Bar</string>
</TestFile>

Я хочу десериализовать его в массив типа «Значение». Этот тип имеет два подтипа «ValueString» и «ValueBool»:

[XmlRoot("TestFile")]
public class TestFile
{
    public List<Test> Tests;
}

public class Value
{
}

public class ValueString : Value
{
    [XmlText]
    public string Value;
}

public class ValueBool : Value
{
    [XmlText]
    public bool Value;
}

Я не могу понять, как это сделать. Я пробовал XmlIncludeAttributes, но этого недостаточно, потому что имена элементов не соответствуют именам классов. Я пробовал XmlChoiceIdentifier, но найденные примеры не компилируются, когда я их адаптирую ...

Мне нужно сохранить порядок элементов, чтобы разделение элементов на два списка не работало. Я также не могу изменить структуру XML, потому что она исходит из внешнего источника. Очевидно, это всего лишь пример - мои настоящие типы более сложные ...

Ответы [ 2 ]

2 голосов
/ 11 апреля 2011

Часто мы сталкиваемся с необходимостью трансформировать предоставленный нам XML-файл. Да, было бы здорово, если бы все подписались на одну и ту же структуру, но в средних и крупных компаниях это может быть труднее достичь.

Я начал с вашего XML-файла:

<?xml version="1.0" encoding="utf-8" ?>
<TestFile>
  <string>Foo</string>
  <bool>false</bool>
  <bool>true</bool>
  <string>Bar</string>
</TestFile>

Затем создал файл преобразования (это только пример и все по вашему усмотрению):

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="@* | node()">
      <xsl:copy>
        <ParameterCollection>
          <xsl:apply-templates select="@* | node()"/>
        </ParameterCollection>
      </xsl:copy>
  </xsl:template>
  <xsl:template match="bool">
    <Parameter type="bool">
      <xsl:apply-templates select="node()"/>
    </Parameter>
  </xsl:template>
  <xsl:template match="string">
    <Parameter type="string">
      <xsl:apply-templates select="node()"/>
    </Parameter>
  </xsl:template>
</xsl:stylesheet>

Затем я начал собирать все необходимые классы вместе:

[XmlRootAttribute("TestFile", IsNullable = false)]
public class TestFile
{
    [XmlArrayAttribute("ParameterCollection")]
    public Parameter[] Parameters;
}

public class Parameter
{
    [XmlAttribute("type")]
    public string ObjectType;

    [XmlText]
    public string ObjectValue;
}

Затем примените все (надеюсь, более вдумчиво, чем я):

class Program
{
    static void Main(string[] args)
    {
        FileInfo xmlFile = new FileInfo(@"Resources\TestFile.xml");
        FileInfo transformFile = new FileInfo(@"Resources\TestFileTransform.xslt");
        FileInfo prettyFile = new FileInfo(@"Resources\PrettyFile.xml");

        if (xmlFile.Exists && transformFile.Exists)
        {
            // Perform transform operations.
            XslCompiledTransform trans = new XslCompiledTransform();
            trans.Load(transformFile.FullName);
            trans.Transform(xmlFile.FullName, prettyFile.FullName);
        }

        if (prettyFile.Exists)
        {
            // Deserialize the new information.
            XmlSerializer serializer = new XmlSerializer(typeof(TestFile));
            XDocument doc = XDocument.Load(prettyFile.FullName);
            TestFile o = (TestFile)serializer.Deserialize(doc.CreateReader());

            // Show the results.
            foreach (Parameter p in o.Parameters)
            {
                Console.WriteLine("{0}: {1}", p.ObjectType, p.ObjectValue);
            }
        }

        // Pause for effect.
        Console.ReadKey();
    }
}

Надеюсь, это кому-нибудь поможет или, по крайней мере, даст им другой вариант. Как правило, ИМХО я бы предпочел разобрать файл или поток, но это только я.

2 голосов
/ 11 апреля 2011

Я, наконец, наткнулся на решение:

[XmlRoot("TestFile")]
public class TestFile
{
    [XmlElement(ElementName = "string", Type = typeof(ValueString))]
    [XmlElement(ElementName = "bool", Type = typeof(ValueBool))]
    public List<Test> Tests;
}

Я думал, что пробовал это раньше ... Ну, по крайней мере, это работает сейчас.В этом примере была еще одна проблема: вы не можете иметь логическое поле, сопоставленное с текстом элемента, но эта конкретная проблема все равно отсутствует в моем истинном сценарии ...

...