【问题标题】:Inject one xml file in the other (merge two xml files) in Visual Studio 2010在 Visual Studio 2010 中将一个 xml 文件注入另一个(合并两个 xml 文件)
【发布时间】:2012-07-08 19:49:50
【问题描述】:

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

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

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

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

有什么想法吗?

【问题讨论】:

  • 您可以尝试使用属性标记不可替换的元素,这样它们就不会在重新生成时被覆盖,如果您可以访问生成部分,您只会告诉它忽略标记有您的属性的元素所以它会跳过它们。
  • 我认为您应该查看资源文件。它们支持自动本地化。
  • @Abhijeetchindhe 这不是我想要的:用 C# 合并两个文件
  • @Michel 麻烦你过来看看答案,看看有没有满意的,如果没有的话留下一些cmets?

标签: c# xml visual-studio-2010 visual-studio xml-parsing


【解决方案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 文件的根节点。

【讨论】:

    【解决方案2】:

    你也可以使用 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");
    

    【讨论】:

    • Michel 在写“@Abhijeetchindhe 这不是我想要的:用 C# 合并两个文件”指的是“c-sharpcorner.com/uploadfile/ae35ca/…”时似乎已经明确表示这不是他想要的。
    • 嗨@CrazyCasta,我没有看到你的消息。我在本文末尾发布了另一个解决方案。
    【解决方案3】:

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

    例如,假设您的 xml 看起来像这样:

    root.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>
    

    generated.xml 和 generated2.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);
                }
            }
        }
    

    【讨论】:

    • 您可能是指那里描述的实体部分吗?我没有看到任何 CDATA 部分,而且 CDATA 无论如何都不会真正有意义(而 ENTITY 会,假设 XmlReader 尊重它)。
    • 你说得对,我指的是实体部分。好像是凌晨 3 点,抱歉:)
    【解决方案4】:

    阅读我认为的 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 的内容插入到输出文件中。

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

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

    【讨论】:

      【解决方案5】:

      还有另一种方法,假设你有以下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));
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-04-24
        • 2012-08-16
        • 2019-03-05
        • 1970-01-01
        • 2012-03-20
        • 1970-01-01
        • 2015-04-12
        • 1970-01-01
        相关资源
        最近更新 更多