How to read the assemblyversion from assemblyInfo.cs? - msbuild

Hii,
there are many others had already post so many question about this..But here the scenario is different.
I need to extract the first three digits ie. $(major).$(Minor).$(Build) from version number.
how can i do this??..i tried AssemblyInfo Task..but that task is just for overwriting the version number.not to extract the version number.
I need to extract first three number and assign them to some property.for further use.
well,i can overwrite them using FileUpdate task.like ::
<FileUpdate
Files="#(AssemblyFile)"
Regex='(\d+)\.(\d+)\.(\d+)\.(\d+)'
ReplacementText='$1.$2.$3.$(Revision)'>
</FileUpdate>
now how can i use their value ie. $1,$2,$3 to assign to properties.???
Thanx.

You can read lines from files, get the string using regex and change it if you need. And if you're using MSBuild 4.0 you can use Property Functions, which give you an access to .NET API.
This example should give you first three numbers of the AssemblyVersion.
<Target Name="ReadAssemblyVersion">
<ReadLinesFromFile File="$(VersionFile)">
<Output TaskParameter="Lines"
ItemName="ItemsFromFile"/>
</ReadLinesFromFile>
<PropertyGroup>
<Pattern>\[assembly: AssemblyVersion\(.(\d+)\.(\d+)\.(\d+)</Pattern>
<In>#(ItemsFromFile)</In>
<Out>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern)))</Out>
</PropertyGroup>
<Message Text="Output : $(Out.Remove(0, 28))"/>
</Target>
http://blogs.msdn.com/b/visualstudio/archive/2010/04/02/msbuild-property-functions.aspx

Awesome thread, building upon the work of Alex and RobPol, I was able to define extended msbuild properties that is inspired by semver.org (Major, Minor, Patch, PreRelease). I chose to parse the AssemblyInformalVersion since that is the only attribute compatible with SemVer. Here is My example:
<PropertyGroup>
<In>$([System.IO.File]::ReadAllText('$(MSBuildProjectDirectory)\Properties\AssemblyInfo.cs'))</In>
<Pattern>\[assembly: AssemblyInformationalVersion\("(?<Major>\d+)\.(?<Minor>\d+)\.(?<Patch>[\d]+)(?<PreReleaseInfo>[0-9A-Za-z-.]+)?</Pattern>
<AssemblyVersionMajor>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern), System.Text.RegularExpressions.RegexOptions.Multiline).Groups["Major"].Value)</AssemblyVersionMajor>
<AssemblyVersionMinor>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern), System.Text.RegularExpressions.RegexOptions.Multiline).Groups["Minor"].Value)</AssemblyVersionMinor>
<AssemblyVersionPatch>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern), System.Text.RegularExpressions.RegexOptions.Multiline).Groups["Patch"].Value)</AssemblyVersionPatch>
<AssemblyVersionPreRelease>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern), System.Text.RegularExpressions.RegexOptions.Multiline).Groups["PreReleaseInfo"].Value)</AssemblyVersionPreRelease>
</PropertyGroup>
You can test the output of this operation by adding the following to your .csproj:
<Target Name="AfterBuild">
<Message Text="$(AssemblyVersionMajor)"></Message>
<Message Text="$(AssemblyVersionMinor)"></Message>
<Message Text="$(AssemblyVersionPatch)"></Message>
<Message Text="$(AssemblyVersionPreRelease)"></Message>
</Target>
Ex: Snippet from my AssemblyInfo.cs:
[assembly: AssemblyInformationalVersion("0.9.1-beta")]
Will output: Major: '0', Minor: '9', Patch: '1', PreRelease: '-beta'

Building on Alex's answer, I used RegEx to read the AssemblyVersion (and other information) and use that in my WiX/MSI filename and version strings. Hopefully my answer isn't too noisy.
Here is the top of my .wixproj file. Points of interest are the first PropertyGroup, OutputName, and DefineConstants:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<In>$([System.IO.File]::ReadAllText('$(MSBuildProjectDirectory)\..\MyApplication\Properties\AssemblyInfoCommon.cs'))</In>
<Pattern>^\s*\[assembly: AssemblyVersion\(\D*(\d+)\.(\d+)\.(\d+)</Pattern>
<AssemblyVersionMajor>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern), System.Text.RegularExpressions.RegexOptions.Multiline).Groups[1].Value)</AssemblyVersionMajor>
<AssemblyVersionMinor>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern), System.Text.RegularExpressions.RegexOptions.Multiline).Groups[2].Value)</AssemblyVersionMinor>
<AssemblyVersionBuild>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern), System.Text.RegularExpressions.RegexOptions.Multiline).Groups[3].Value)</AssemblyVersionBuild>
<Pattern>^\s*\[assembly: AssemblyDescription\(\s*"([^"]+)"</Pattern>
<AssemblyDescription>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern), System.Text.RegularExpressions.RegexOptions.Multiline).Groups[1].Value)</AssemblyDescription>
<Pattern>^\s*\[assembly: AssemblyProduct\(\s*"([^"]+)"</Pattern>
<AssemblyProduct>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern), System.Text.RegularExpressions.RegexOptions.Multiline).Groups[1].Value)</AssemblyProduct>
<Pattern>^\s*\[assembly: AssemblyCompany\(\s*"([^"]+)"</Pattern>
<AssemblyCompany>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern), System.Text.RegularExpressions.RegexOptions.Multiline).Groups[1].Value)</AssemblyCompany>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>3.7</ProductVersion>
<ProjectGuid>MYGUID00-840B-4055-8251-F2B83BC5DBB9</ProjectGuid>
<SchemaVersion>2.0</SchemaVersion>
<OutputName>$(AssemblyProduct)-$(AssemblyVersionMajor).$(AssemblyVersionMinor).$(AssemblyVersionBuild)</OutputName>
<OutputType>Package</OutputType>
<WixTargetsPath Condition=" '$(WixTargetsPath)' == '' AND '$(MSBuildExtensionsPath32)' != '' ">$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
<WixTargetsPath Condition=" '$(WixTargetsPath)' == '' ">$(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>Debug;AssemblyVersionMajor=$(AssemblyVersionMajor);AssemblyVersionMinor=$(AssemblyVersionMinor);AssemblyVersionBuild=$(AssemblyVersionBuild);AssemblyDescription=$(AssemblyDescription);AssemblyProduct=$(AssemblyProduct);AssemblyCompany=$(AssemblyCompany)</DefineConstants>
<SuppressValidation>False</SuppressValidation>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>AssemblyVersionMajor=$(AssemblyVersionMajor);AssemblyVersionMinor=$(AssemblyVersionMinor);AssemblyVersionBuild=$(AssemblyVersionBuild);AssemblyDescription=$(AssemblyDescription);AssemblyProduct=$(AssemblyProduct);AssemblyCompany=$(AssemblyCompany)</DefineConstants>
</PropertyGroup>
And then in a .wxi file I have this:
<?define MajorVersion="$(var.AssemblyVersionMajor)" ?>
<?define MinorVersion="$(var.AssemblyVersionMinor)" ?>
<?define BuildVersion="$(var.AssemblyVersionBuild)" ?>
<?define VersionNumber="$(var.MajorVersion).$(var.MinorVersion).$(var.BuildVersion)" ?>
And finally in my Product.wxs:
<?include Definitions.wxi ?>
<Product Id="$(var.GuidProduct)" Name="$(var.AssemblyProduct) $(var.VersionNumber)" Language="!(loc.LANG)"
Version="$(var.VersionNumber)" Manufacturer="$(var.AssemblyCompany)" UpgradeCode="$(var.GuidUpgrade)">
<Package Id="$(var.GuidPackage)" InstallerVersion="301" Compressed="yes" InstallScope="perMachine"
Keywords="!(loc.Keywords)" Description="$(var.AssemblyProduct)" Comments="$(var.AssemblyDescription)" />

If you want to be able to process your AssemblyInfo file with 100% accuracy you can use a C# task + Roslyn.
public class ReadAssemblyInfo : Task {
[Required]
public string AssemblyInfoFilePath { get; set; }
[Output]
public TaskItem AssemblyVersion { get; set; }
[Output]
public TaskItem AssemblyInformationalVersion { get; set; }
[Output]
public TaskItem AssemblyFileVersion { get; set; }
public override bool Execute() {
using (var reader = new StreamReader(AssemblyInfoFilePath)) {
var text = reader.ReadToEnd();
var tree = CSharpSyntaxTree.ParseText(text);
var root = (CompilationUnitSyntax)tree.GetRoot();
var attributeLists = root.DescendantNodes().OfType<AttributeListSyntax>();
foreach (var p in attributeLists) {
foreach (var attribute in p.Attributes) {
var identifier = attribute.Name as IdentifierNameSyntax;
if (identifier != null) {
var value = ParseAttribute("AssemblyInformationalVersion", identifier, attribute);
if (value != null) {
SetMetadata(AssemblyInformationalVersion = new TaskItem(value.ToString()), value);
break;
}
value = ParseAttribute("AssemblyVersion", identifier, attribute);
if (value != null) {
SetMetadata(AssemblyVersion = new TaskItem(value.ToString()), value);
break;
}
value = ParseAttribute("AssemblyFileVersion", identifier, attribute);
if (value != null) {
SetMetadata(AssemblyFileVersion = new TaskItem(value.ToString()), value);
break;
}
}
}
}
}
return !Log.HasLoggedErrors;
}
private void SetMetadata(TaskItem taskItem, Version version) {
taskItem.SetMetadata(nameof(version.Major), version.Major.ToString());
taskItem.SetMetadata(nameof(version.Minor), version.Minor.ToString());
taskItem.SetMetadata(nameof(version.Build), version.Build.ToString());
taskItem.SetMetadata(nameof(version.Revision), version.Revision.ToString());
}
private static Version ParseAttribute(string attributeName, IdentifierNameSyntax identifier, AttributeSyntax attribute) {
if (identifier.Identifier.Text.IndexOf(attributeName, StringComparison.Ordinal) >= 0) {
AttributeArgumentSyntax listArgument = attribute.ArgumentList.Arguments[0];
var rawText = listArgument.Expression.GetText().ToString();
if (!string.IsNullOrWhiteSpace(rawText)) {
rawText = rawText.Replace("\"", "");
Version version;
if (Version.TryParse(rawText, out version)) {
return version;
}
}
}
return null;
}
}

I just found this on google, might help:
http://msdn.microsoft.com/en-us/library/system.reflection.assemblyname.version%28v=VS.90%29.aspx
Particularly:
//For AssemblyFileVersion
Assembly asm = Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(asm.Location);
string version = fvi.FileVersion
//For AssemblyVersion
string revision = Assembly.GetExecutingAssembly().GetName().Version.Revision;

You can use MSBuild.Community.Tasks.AssemblyInfo.AssemblyVersion to access AssemblyVersion and AssemblyFileVersion from AssemblyInfo.cs.
Indeed this task can only be used to set the version.
Maybe this post is usefull.

I use the RegexMatch-task from the MSBuild.Community.Tasks.
You can write the output of the match to an itemgroup, although you want to read it into 3 properties, as above, a custom task would then be prefered.

The only solution is to write a custom build task, and parse the version number manually in the code.

Related

Can Exec task in msbuild fail if exitCode = 0 but there is logging to standard error?

I am curious about:
https://learn.microsoft.com/en-us/visualstudio/msbuild/exec-task?view=vs-2019
ExitCode Optional Int32 output read-only parameter.
Specifies the exit code that is provided by the executed command, except that if the task logged any errors, but the process had an exit code of 0 (success), ExitCode is set to -1.
Can msbuild fail on Exec even if process has an exit code of 0 but it logs to standard error?
I tried to make it fail with the following code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program
{
static int Main( string[] args )
{
using ( var stdErr = Console.Error )
{
for ( int i = 0; i <= 100; i++ )
{
stdErr.WriteLine( $"Failed to copy attempt {i}." );
System.Threading.Thread.Sleep( 50 );
}
// Close redirected error stream.
Console.Error.Close();
}
return 0;
}
}
}
It is being called as part of a standard props file.
<Target
Name = "IvaraSign"
AfterTargets="CoreBuild"
BeforeTargets="CopyFilesToOutputDirectory" >
<!-- Update version on main output -->
<!-- this works when the solution being compiled is in the dotnet folder. -->
<Exec Command="C:\Users\Derek.Morin\source\repos\ConsoleApp3\bin\Debug\ConsoleApp3.exe"/>
</Target>
When I build in msbuild I can see the error text in the build output but the compiling passes.
Does the MSBuild Exec task search STDOUT for the string "error"?
I was able to duplicate making exec file fail with the following
static int Main( string[] args )
{
using ( var stdErr = Console.Error )
{
stdErr.WriteLine( $#"signtool.exe : error information: ""Error: Store IsDiskFile() failed."" (-2147024864/0x80070020) [d:\work\1\s\common\MAFPlugins\MAFScripting\Contracts\Contracts.csproj]" );
}
Console.WriteLine( "but I'm still going to return 0" );
return 0;
}
The workaround is to use IgnoreStandardErrorWarningFormat="true".
<Exec Command="C:\ConsoleApp3\ConsoleApp3.exe" IgnoreStandardErrorWarningFormat="true" />

Visual Studio 2019 .NET Core 3 gRpc not generating service files

I created a simple gRpc server application using the template in Visual Studio 2019. I then created a protobuf file for the server but I cannot get it to generate the corresponding service files.
The protobuf file is:
syntax = "proto3";
option csharp_namespace = "RpcApi";
import "google/protobuf/timestamp.proto";
package API;
service Conferences {
rpc GetAll (GetAllConferencesRequest) returns (GetAllConferencesResponse) {}
rpc Add (AddConferenceRequest) returns (AddConferenceResponse) {}
}
message GetAllConferencesRequest {}
message GetAllConferencesResponse {
repeated Conference Conferences = 1;
}
message AddConferenceRequest {
Conference conferernce = 1;
}
message AddConferenceResponse {
Conference conference = 1;
}
message Conference {
int32 Id = 1;
string Name = 2;
google.protobuf.Timestamp Start = 3;
string Location = 4;
int32 AttendeeTotal = 5;
}
service Proposals {
rpc GetAll (GetAllProposalsRequest) returns (GetAllProposalsResponse) {}
rpc Add (AddProposalRequest) returns (AddProposalResponse) {}
rpc Approve (ApproveRequest) returns (ApproveResponse) {}
}
message GetAllProposalsRequest {
int32 ConferenceId = 1;
}
message GetAllProposalsResponse {
repeated Proposal Proposals = 1;
}
message AddProposalRequest {
Proposal Proposal = 1;
}
message AddProposalResponse {
Proposal Proposal = 1;
}
message ApproveRequest {
int32 Id = 1;
}
message ApproveResponse {
Proposal Proposal = 1;
}
message Proposal {
int32 Id = 1;
int32 ConferenceId = 2;
string Speaker = 3;
string Title = 4;
bool Approved = 5;
}
And the project file is correctly defined:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Protobuf Include="Protos\rpcapi.proto" GrpcServices="Server" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.23.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="Services\" />
</ItemGroup>
</Project>
Its supposed to generate server-side files whenever the proto file changes but it never generates those files. What is wrong?
It actually works. The files are generated in the obj directory and not the Services directory. If I do Show All Files, I can see them.

ant store variable in scriptdef or call task in script def

My knowledge of ant is close to nothing.
i do understand it should not be used as a programming language but i'm a consumer of a certain ant project and want to modify something in my own project while using the libraries that project offers me.
the main point i want to do is I have a string and need to modify it before sending it to the parent project target.
i'll try providing a code easy to understand, but at the moment the part i have left is:
either store the value in a variable instead of a property (not sure how to do this)
directly call the other target from my javascript function.
so this is the code:
<target name="deploy-custom" depends="init">
<scriptdef name="replaceString" language="javascript">
<attribute name="fileIn" />
<attribute name="directoryFile" />
<![CDATA[echo = project.createTask("echo");
var fileName = attributes.get("filein"); //get attribute for scriptdef
var directoryIn = attributes.get("directoryfile"); //get attribute for scriptdef
echo.setMessage("file name: " + fileName );
echo.perform( );
echo.setMessage("dir in " + directoryIn );
echo.perform( );
var fileOut = fileName.replace(directoryIn, "");
echo.setMessage("replace " + fileOut );
echo.perform( );
project.setProperty("undeploy_name", fileOut);]]>
</scriptdef>
<echo message="executing target deploy-custom" />
<for param="file">
<path>
<fileset dir="${mydir}/content/custom-deploy">
<include name="*.war" />
</fileset>
</path>
<sequential>
<replaceString fileIn="#{file}" directoryFile="${mydir}/content/custom-deploy/" />
<JBossCLI port="${jboss.port.management-native}">
<undeploy namePattern="${undeploy_name}" />
</JBossCLI>
<deployToLiferay file="#{file}" />
</sequential>
</for>
<echo>ABRS custom banklets deployed!</echo>
</target>
so my question is at the time i try to save the undeploy_name property can I just call the target deployToLiferay? if not is there a way i can save that in a variable instead a property?
i don't mind using other language instead of javascript but not really sure how can i do what i need to do.
based on the info i found in here i'm now trying to focus on the using the script directly. this is the info i get:
https://coderanch.com/t/108191/call-ant-macrodef-groovy-script
i tried to modify my script to something like this:
<macrodef name="undeploy">
<attribute name="undplPattern" />
<sequential>
<echo message="undeploy undplPattern #{undplPattern}" />
<JBossCLI port="${jboss.port.management-native}">
<undeploy namePattern="#{undplPattern}" />
</JBossCLI>
</sequential>
</macrodef>
<scriptdef name="undeploy-pattern" language="javascript">
<attribute name="fileIn" />
<attribute name="directoryFile" />
<![CDATA[
var echo = project.createTask("echo");
var fileName = attributes.get("filein"); //get attribute for scriptdef
var directoryIn = attributes.get("directoryfile"); //get attribute for scriptdef
echo.setMessage("file name: " + fileName );
echo.perform( );
echo.setMessage("dir in " + directoryIn );
echo.perform( );
var fileOut = fileName.replace(directoryIn, "");
fileOut = fileOut.replace(/\d+/g, "");
fileOut = fileOut.replace("..",".*");
fileOut = fileOut.replace(/[.]/g,"\\.");
fileOut = fileOut.replace("web-\\.*\\.war","web.*");
echo.setMessage("undeploy pattern transformation: " + fileOut );
echo.perform( );
var undeploy_t = project.createTask("undeploy");
undeploy_t.setDynamicAttribute("undplPattern", fileOut);
undeploy_t.perform( );
]]>
</scriptdef>
called from:
<echo message="item #{file}" />
<undeploy-pattern fileIn="#{file}" directoryFile="${currentScriptDirectory}/content/custom-banklets/" />
<deployToLiferay file="#{file}" />
after this modifications it now fails when i try to set setDynamicAttribute and perform that task.
08:01:18.492: item /data/com.client-dshbrd-banklet-web-0.0.1.war
08:01:18.509: file name: /data/com.client-dshbrd-banklet-web-0.0.1.war
08:01:18.510: dir in /data/
08:01:18.520: undeploy pattern transformation: com\.client-dshbrd-banklet-web.*
08:01:18.528: COMMAND 'deploy-custom-banklets' FAILED (execution time: 2 seconds)
08:01:18.528: * /data/contribution.xml:250: The following error occurred while executing this line:
08:01:18.528: * /data/contribution.xml:259: required attribute undplpattern not set
I don't think you need an embedded script. I reviewed the logic and I think it would be simpler to use the ANT basename task in order to obtain the file name.
Example
├── build.xml
└── src
└── files
└── file1.war
Project run as follows
$ ant
build:
[echo] file1.war
build.xml
<project name="demo" default="build">
<target name="build">
<basename property="undeploy_name" file="src/files/file1.war"/>
<echo>${undeploy_name}</echo>
</target>
</project>

msbuild solution file

I have created a msbuild file which will build a solution file(NOTE: not a project file). I would like to be able to change the debug and release target path in the msbuild file. How can i go about doing that? Thank you very much.
msbuild file
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="deploy">
<MSBuild Projects="foo.sln" Properties="Configuration=Release" ContinueOnError="false" />
</Target>
</Project>
Solution file Below:
Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "foo", ".", "{3D958438-10F1-4211-BC7F-F0A5E5601C3F}"
ProjectSection(WebsiteProperties) = preProject
TargetFramework = "3.5"
Debug.AspNetCompiler.VirtualPath = "/foo"
Debug.AspNetCompiler.PhysicalPath = "..\foo\"
Debug.AspNetCompiler.TargetPath = "..\..\PrecompiledWeb\foo\"
Debug.AspNetCompiler.Updateable = "true"
Debug.AspNetCompiler.ForceOverwrite = "true"
Debug.AspNetCompiler.FixedNames = "false"
Debug.AspNetCompiler.Debug = "True"
Release.AspNetCompiler.VirtualPath = "/foo"
Release.AspNetCompiler.PhysicalPath = "..\foo\"
Release.AspNetCompiler.TargetPath = "..\..\PrecompiledWeb\foo\"
Release.AspNetCompiler.Updateable = "true"
Release.AspNetCompiler.ForceOverwrite = "true"
Release.AspNetCompiler.FixedNames = "false"
Release.AspNetCompiler.Debug = "False"
VWDPort = "51644"
DefaultWebSiteLanguage = "Visual Basic"
EndProjectSection
EndProject
To change the output, set the OutDir property. So instead of:
Properties="Configuration=Release"
try:
Properties="Configuration=Release;OutDir=d:\myCode\out\"

WCF DataContractSerializer Behavior

I'm seeing some unusual behavior when using the DataContractSerializer. I have defined a message contract like so:
namespace MyNamespace.DataContracts
{
[MessageContract(WrapperName = "order", WrapperNamespace = #"http://example.com/v1/order")]
public class MyOrder
{
[MessageBodyMember(Namespace = #"http://example.com/v1/order", Order = 1)]
public MyStore store;
[MessageBodyMember(Namespace = #"http://example.com/v1/order", Order = 2)]
public MyOrderHeader orderHeader;
[MessageBodyMember(Namespace = #"http://example.com/v1/order", Order = 3)]
public List<MyPayment> payments;
[MessageBodyMember(Namespace = #"http://example.com/v1/order", Order = 4)]
public List<MyShipment> shipments;
}
.
.
I'm sending it an XML message that looks like this:
<?xml version="1.0" encoding="utf-8"?>
<order xmlns="http://example.com/v1/order>
<store>
...
</store>
<orderHeader>
...
</orderHeader>
<payments>
<payment>
...
</payment>
</payments>
<shipments>
<shipment>
...
</shipment>
</shipments>
</order>
My service deserializes this XML as expected. Inside my service, I'm using the DataContractSerializer to create an XML string and that's where things get weird. I'm using the serializer like this:
DataContractSerializer serializer = new DataContractSerializer(typeof(MyOrder));
using (MemoryStream ms = new MemoryStream())
{
serializer.WriteObject(ms, order);
ms.Position = 0;
StreamReader sr = new StreamReader(ms);
string outputMessage = sr.ReadToEnd();
}
Once this finishes, the outputMessage contains the following XML:
<?xml version="1.0" encoding="utf-8"?>
<MyOrder xmlns="http://example.com/v1/order" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<order>
<store>
...
</store>
<orderHeader>
...
</orderHeader>
<payments>
<payment>
...
</payment>
</payments>
<shipments>
<shipment>
...
</shipment>
</shipments>
</order>
</MyOrder>
Needless to say, anything expecting to receive the original XML message will fail to parse this. So I guess I have two questions:
Why is the DataContractSerializer
adding the extra outer node to my
XML output?
Is there a way to stop it from doing
this?
Thanks.
I should probably add this is with .NET 4.
You could try using WriteObjectContent instead of WriteObject, but I'm unable to reproduce your problem using the code you supplied. All the extra class defintions that are part of your message contract are empty in my definition, but this is the XML I am getting:
<MyOrder xmlns="http://schemas.datacontract.org/2004/07/SandboxApp"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<orderHeader i:nil="true"/>
<payments i:nil="true"/>
<shipments i:nil="true"/>
<store i:nil="true"/>
</MyOrder>
Which also seems odd, since it seems to ignore the WrapperName. Same result in .NET 3.5 SP1 and .NET 4.0.