2

我正在做一个项目,我必须将翻译放在 XML 中。现在 xml 翻译文件是自动生成的,但不适用于我的项目部分。我和他们必须共享生成的 XML。

因此,当我在他们的“主要”XML 中输入我的一段 XML 时,它会在文件重新生成时被覆盖。当前的解决方案是为我的翻译保留一个单独的文件,并每次将其复制粘贴到“主”XML 文件中。

有没有办法自动做到这一点?我尝试了 xmlInclude选项,但 Visual Studio 无法识别或使用它。

我考虑过在“主”XML 中生成某种占位符,并且 Visual Studio 会“以某种方式”用我单独文件中的 XML 替换占位符。

有任何想法吗?

4

5 回答 5

3

有一种方法可以将 xml 文件包含在此处描述的 ENTITY 部分中。如果 .Net XmlReader 没有自动执行此操作,您需要自己实现一个 XmlResolver 并将其设置到 XmlReaderSettings 中,然后再读取您的根文件。覆盖 ResolveUri 就足够了。

例如,假设您的 xmls 如下所示:

根.xml:

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE root [
  <!ENTITY generatedXML SYSTEM "generated.xml">
  <!ENTITY generatedXML2 SYSTEM "pack://application:,,,/generated2.xml">
]>
<root>
  &generatedXML;
  &generatedXML2;
  <extraContent>
    <extraValue Name="xa" Value="1"/>
    <extraValue Name="xb" Value="1"/>
    <extraValue Name="xc" Value="1"/>
    <extraValue Name="xd" Value="1"/>
  </extraContent>
</root>

生成的.xml 和生成的2.xml:

<?xml version="1.0" encoding="utf-8" ?>
<generatedContent>
  <generatedValue Name="a" Value="1"/>
  <generatedValue Name="b" Value="2"/>
  <generatedValue Name="c" Value="3"/>
  <generatedValue Name="d" Value="4"/>
</generatedContent>

其中“generated.xml”位于根 xml 旁边,“generated2.xml”位于应用程序程序集中的其他位置,并标有“BuildAction = Resource”。然后我们可以像这样写一个 XmlResolver:

using System;
using System.IO;
using System.IO.Packaging;
using System.Net;
using System.Xml;

namespace XmlResolution
{
    class XmlResourceResolver : XmlResolver
    {
        private static readonly string FileScheme = "file";
        private static readonly string PackScheme = PackUriHelper.UriSchemePack;
        private ICredentials credentials;

        public override ICredentials Credentials
        {
            set { this.credentials = value; }
        }

        public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
        {
            if (absoluteUri.Scheme == FileScheme)
            {
                return File.OpenRead(absoluteUri.LocalPath);
            }
            else if (absoluteUri.Scheme == PackScheme)
            {
                return System.Windows.Application.GetResourceStream(absoluteUri).Stream;
            }

            return null;
        }
    }
}

最后我们可以像这样读取我们的xml:

    static void Main(string[] args)
    {
        var settings = new XmlReaderSettings
        {
            DtdProcessing = DtdProcessing.Parse,
            XmlResolver = new XmlResourceResolver()
        };

        using (var reader = XmlReader.Create("root.xml", settings))
        {
            var element = XElement.Load(reader);

            var nameValuePairs = from content in element.Elements()
                                 from value in content.Elements()
                                 select new
                                 {
                                     Name = value.Attribute("Name"),
                                     Value = int.Parse(value.Attribute("Value").Value)
                                 };

            foreach (var pair in nameValuePairs)
            {
                Console.WriteLine(pair.Name + " " + pair.Value);
            }
        }
    }
于 2012-09-29T22:41:02.323 回答
1

您可以执行以下操作(我不确定您是如何阅读它的,所以我假设您可以使用 XmlDocument,如果不是,您可能可以将 XmlDocument 转换为您需要的 w/e):

ArrayList xmlNodes;
XmlNode newNode;

XmlDocument xmlDoc = new XmlDocument();
XmlDocument toInclude = new XmlDocument();

xmlDoc.Load("myFile.xml");
toInclude.Load("toInclude.xml");

xmlNodes = new ArrayList();

foreach (XmlElement ele in xmlDoc.GetElementsByTagName("toReplace"))
{
    xmlNodes.Add(ele);
}

foreach (XmlElement ele in xmlNodes)
{
    ele.ParentNode.ReplaceChild(xmlDoc.ImportNode(toInclude.DocumentElement, true), ele);
}

这将用 toInclude.xml 文件的根节点替换任何标记。

于 2012-09-28T15:53:46.427 回答
1

阅读我认为的 OP:能够关联到 Visual Studio 项目中的任何 xml 文件不是很好吗 - 一个用于转换此文件的 XSL 文件。此转换的结果将显示为原始 xml 文件下的子项,例如 *.xaml.cs 出现在 *.xaml 下。我们将能够通过 Properties 将任何 Build Action 分配给这个新的 xml 文件。

现在这是不可能的,但存在另一种更通用的预处理技术 - T4。以下是如何使用它将“我们的”xml 片段插入“他们的”生成的 xml 文件的描述。

1) 假设“他们”生成的 xml 文件包含在我们的项目中,名称为 aaa.xml。

例如aaa.xml

<?xml version="1.0" encoding="utf-8"?>
<abc>
  <data a="1"/>
  <data a="2"/>
  <placeholderForTranslations/>
  <data>ccc</data>
</abc>

其中 placeholderForTranslations 元素需要替换为“我们的”片段。

2) 让我们将 aaa.xslt 文件添加到我们的项目中,该文件通过将 placeholderForTranslations 替换为“我们的”片段来转换 aaa.xml。

aaa.xslt

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="placeholderForTranslations">
    <translations>
      <trans orig="aaa" translated="AAA"/>
    </translations>
  </xsl:template>

</xsl:stylesheet>

这里的第一个 xsl:template 表示默认情况下,转换引擎会遍历输入文件的节点,它只是将它们原样复制到输出中。第二个模板表示,如果遇到 placeholderForTranslations 元素,嵌入到模板中的 xml 片段将用于替换 placeholderForTranslations 元素。事实上,如果您愿意,可以修改 *.tt 文件(见下文),以便也可以从单独的文件中读取此片段。

3) 让我们将 aaa1.tt - t4 代码预处理器文件添加到我们的项目中。

aaa1.tt

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".xml" #>
<#@ assembly name="System.Xml" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Xml.Xsl" #>
<#
    string origXmlPath = Host.ResolvePath("aaa.xml");
    string transformPath = Host.ResolvePath("aaa.xslt");

    XslCompiledTransform transform = new XslCompiledTransform();
    transform.Load(transformPath);
    MemoryStream stream = new MemoryStream();
    transform.Transform(origXmlPath, null, stream);
    byte[] generated = stream.ToArray();
    string generatedString = Encoding.UTF8.GetString(generated);
#>
<#= generatedString #>

在 <# 和 #> 之间,您可以看到一段 C# 代码,它找到 aaa.xml 和 aaa.xslt 的完整路径,然后将 aaa.xslt 转换应用于 aaa.xml 文件并将结果放入 generatedString 变量中。然后在 <#= generatedString #> 的帮助下,将 generatedString 的内容插入到输出文件中。

如果执行这些步骤,在 Visual Studio 中首次保存 aaa1.tt 时,文件 aaa1.xml 将创建为 aaa1.tt 的子项。您可以通过其属性为该文件分配构建操作,例如“嵌入式资源”。您也可以随时通过右键单击 aaa.tt 并选择“运行自定义工具”来强制重新生成 aaa1.xml。当您构建解决方案时,它也总是会重新生成。

我认为这种方法的优点是您的合并文件是在编译时生成的 - 而不是在应用程序运行时生成的。因此可以在开发过程中目视检查文件。

于 2012-10-02T15:30:35.083 回答
1

您也可以使用 DataSet 对象。

    XmlTextReader readerOne = new XmlTextReader("C:\\generated.xml");
    XmlTextReader readerTwo = new XmlTextReader("C:\\your-file.xml");

    DataSet dsOne = new DataSet();
    dsOne.ReadXml(readerOne);

    DataSet dsTwo = new DataSet();
    dsTwo.ReadXml(readerTwo);

    dsOne.Merge(dsTwo);
    dsOne.WriteXml("C:\\CompleteResource.xml");
    Console.WriteLine("Merge completed");
于 2012-09-28T22:00:18.260 回答
0

还有另一种方法,假设您有以下 xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="ApplicationMainWindow_Button_RunWizard" xml:space="preserve">
    <value>Ordenar Café</value>
  </data>
  <data name="ApplicationMainWindow_OrderCancelled" xml:space="preserve">
    <value>Orden Cancelada</value>
  </data>
  <data name="ApplicationMainWindow_OrderComplete_Formatted" xml:space="preserve">
    <value>El costo de la tasa de café es de {0}, gracias.</value>
  </data>
</root>

并且您想与以下内容合并:

<?xml version="1.0" encoding="utf-8" ?>
<root>
  <data name="ApplicationMainWindow_Title" xml:space="preserve">
    <value>Café de casa</value>
  </data>
  <data name="BeanType_Breakfast" xml:space="preserve">
    <value>Grand Aroma</value>
  </data>
</root>

然后你可以编写类似的代码:

public class XmlManager
{
    private XDocument _document1;
    private XDocument _document2;
    private string _autogenerated;
    private string _manual;
    private string _target;
    public XmlManager()
    {
        Initialize();
    }

    private void Initialize()
    {
        _autogenerated = @"c:\AutogeneratedResource.xml";
        _manual = @"c:\ManualResource.xml";

        XmlDocument doc = new XmlDocument();
        doc.Load(_autogenerated);
        _document1 = XDocument.Parse(doc.InnerXml);

        doc.Load(_manual);
        _document2 = XDocument.Parse(doc.InnerXml);

        _target = @"c:\Target.xml";
    }

    public void Save()
    {
        if (_document2.Root != null)
        {
            IEnumerable<XElement> elements = _document2.Root.Elements("data");
            if (_document1.Root != null)
                _document1.Root.Add(elements);

            _document1.Save(new StreamWriter(_target));
        }
    }
}
于 2012-10-06T06:21:59.010 回答