m0sa
m0sa

Reputation: 10940

Use XSD Build task in c# project

How can I use the c++ XSD Task in a c# project? I have already created the task in the csproj file like this:

  <Target Name="BeforeBuild">
    <XSD Namespace="$(RootNamespace).Xml" Language="CS" GenerateFromSchema="classes" Sources="Xml/schema.xsd" />
  </Target>

but the build output says, although intellisense offers me the XSD task while editing the project file:

Error   1   The "XSD" task was not found. Check the following: 
1.) The name of the task in the project file is the same as the name of the task class. 
2.) The task class is "public" and implements the Microsoft.Build.Framework.ITask interface. 3.) The task is correctly declared with <UsingTask> in the project file, or in the *.tasks files located in the "C:\Windows\Microsoft.NET\Framework\v4.0.30128" directory. MyProject.csproj

Oh, and I am using Visual Studio 2010 RC.

*

Upvotes: 6

Views: 5546

Answers (5)

Ted Kerley
Ted Kerley

Reputation: 11

It looks like the XSD MsBuild task has been deprecated since VS2017. The following target will call Xsd.exe using the Exec task before compile.

 <!--
      Executes the xsd.exe command on Filename.xsd.  If more XSD files
      are required, this target should be modified to find and generate 
      classes for all XSD classes, or alternatively use an MsBuild item
      to define which XSD files to use.
   -->
 <Target Name="Xsd" BeforeTargets="BeforeCompile" Inputs="Folder\Filename.xsd" Outputs="Folder\Filename.cs">
    <Message Importance="high" Text="Executing Xsd.exe on Folder\Filename.xsd"/>
    <!--
      Note the XSD msuild task is deprecated and removed.
      So execute the command using the Exec task.
     -->
    <Exec Command="&quot;$(SDK40ToolsPath)Xsd.exe&quot; /classes &quot;Folder\Filename.xsd&quot; /outputdir:Folder /namespace:Root.Folder"/>
  </Target>

Update: The following msbuild snippet can be used for transforming a list of xsd items with namespaces reflecting the file structure. Generation is ignored if already generated.

    <!-- Define the location of xml and schema files. -->
    <PropertyGroup>
        <XmlConfigurationFolder>Configuration\Xml\</XmlConfigurationFolder>
    </PropertyGroup>
    
    <!-- Detail the schema files to be transformed.  Each file's included or imported dependencies are also listed. -->
    <ItemGroup>
        <XmlSchema Include="$(XmlConfigurationFolder)Schema1.xsd">
            <DependentOn>$(XmlConfigurationFolder)Import1.xsd;$(XmlConfigurationFolder)Include1.xsd;</DependentOn>
        </XmlSchema>
        <XmlSchema Include="$(XmlConfigurationFolder)Schema2.xsd">
            <DependentOn>$(XmlConfigurationFolder)Import1.xsd;$(XmlConfigurationFolder)Import2.xsd;</DependentOn>
        </XmlSchema>
    </ItemGroup>
    
    <!-- Ensure all xml files with associated schema files are copied to the output folder. -->
    <ItemGroup>
        <ContentWithTargetPath Include="$(XmlConfigurationFolder)*.xsd;$(XmlConfigurationFolder)*.xml">
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
            <TargetPath>%(Filename)%(Extension)</TargetPath>
        </ContentWithTargetPath>
    </ItemGroup>
    
    <!--
        Target to transform schema files to c# classes.
        This target may be sited in Directory.Build.targets if more than one project has xml schemas to transform.
        Use target batching to enable rebuild on dependency change (do not rebuild all when any dependency changes). 
        Do not use BeforeTargets="Build" or BeforeTargets="Compile".  Instead use BeforeTargets="BeforeBuild" or BeforeTargets="BeforeCompile".
    -->
    <Target Name="XsdToClasses" BeforeTargets="BeforeCompile" Inputs="@(XmlSchema);%(DependentOn);" Outputs="@(XmlSchema->'%(RelativeDir)%(Filename).cs')">
        <Message Importance="high" Text="Transforming @(XmlSchema)"/>
        <PropertyGroup>
            <Filename>@(XmlSchema->'%(RelativeDir)%(Filename).cs')</Filename>
        </PropertyGroup>

        <!--
          The XSD msuild task is deprecated and removed. Execute the command using the Exec task.
          Use task batching to execute each command individually.
          The c# namespace is generated from the folder in which the files reside (spaces are converted to underscores).
          -->
          <Exec Command="&quot;$(SDK40ToolsPath)Xsd.exe&quot; /nologo /classes &quot;@(XmlSchema)&quot;  &quot;/outputdir:%(RelativeDir).&quot; /namespace:$(RootNamespace).@(XmlSchema-&gt;'%(RelativeDir)'-&gt;Replace('\','.')-&gt;Replace(' ','_')-&gt;TrimEnd('.'))" />
    
        <!-- Add the new file to the compilation list if not already in it.  -->
        <!-- It is not clear why the condition clause is needed, since this target should not execute when the outputs are up to date. -->
        <ItemGroup>
            <Compile Include="$(Filename)" Condition="!(@(Compile->AnyHaveMetadataValue('Identity', $(Filename))))"/>
        </ItemGroup>
    </Target>

Upvotes: 1

Sherlock
Sherlock

Reputation: 116

A slightly improved version/approach is to use the following in a common .targets file:

<!-- Establish the proper $(CPPTasks) location -->
<Choose>
  <When Condition=" '_$(VisualStudioVersion)'=='_11.0' " >
    <PropertyGroup>
        <CPPTasks>V110\Microsoft.Build.CPPTasks.Common.v110.dll</CPPTasks>
    </PropertyGroup>
  </When>
  <Otherwise>
    <PropertyGroup>
        <CPPTasks>Microsoft.Build.CPPTasks.Common.dll</CPPTasks>
    </PropertyGroup>
  </Otherwise>
</Choose>

Then in your .csproj you can do the following:

<UsingTask TaskName="XSD" AssemblyFile="$(VCTargetsPath)$(CPPTasks)" />

Note that this has been tested in VS2010, VS2011, VS2013 (DLL names switch back and forth depending on the version of VS installed. :(

The improvement is that only one place contains the logic for the proper DLL path and name rather than individual .csproj files.

Upvotes: 4

Howard Hoffman
Howard Hoffman

Reputation: 917

Got it to work with a slightly different syntax. This is using VS 2012 Update 4.

What worked:

<UsingTask TaskName="XSD" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft.Cpp\v4.0\V110\Microsoft.Build.CPPTasks.Common.v110.dll" />
<Target Name="BeforeBuild">
    <Message Text="Generating serialization class(es)..." />
    <ItemGroup>
        <_SerializationFile Include="$(ProjectDir)My_Xsd_File.cs" />
    </ItemGroup>
    <XSD Condition="!Exists('@(_SerializationFile)')" GenerateFromSchema="classes" Language="CS" Namespace="$(RootNamespace)" Sources="My.Xsd.File.xsd" />
</Target>

I used the direct AssemblyFile attribute on UsingTask in my case. Note also that embedded . characters in the XSD name were converted to _ characters as well.

Upvotes: 2

huoxudong125
huoxudong125

Reputation: 2056

Add this to your project file:

<UsingTask TaskName="XSD" AssemblyName="Microsoft.Build.CppTasks.Common, Version=4.0.0.0,         Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
 <Target Name="BeforeBuild">
  <XSD Sources="MySchema.xsd" GenerateFromSchema="classes" Language="CS">
  </XSD>
 </Target>

Please look the Question Using XSD task I'm getting MSB0001: Internal MSBuild Error: xsd.exe unexpectedly not a rooted path

And add the path of xsd.exe and Microsoft.Build.CppTasks.Common.dll to Environment Variable, restart my computer. It works fine again.

The path of xsd.exe and Microsoft.Build.CppTasks.Common.dll are:

C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\; C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0

Or you also can add following command line in your Pre-build event to achieve this:

xsd.exe /classes /language:CS MySchema.xsd

Upvotes: 1

Jamie
Jamie

Reputation: 41

This is because the C++ targets are not imported. Because XSD is not one of the default tasks defined by the managed build process. you need to add a UsingTask to reference it. Add this to your csproj file:

<UsingTask TaskName="XSD" AssemblyName="Microsoft.Build.CppTasks.Common, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>

Good Luck, Jamie

Upvotes: 4

Related Questions