csharp, dotnet comments edit

The move from Newtonsoft to System.Text.Json for JSON serialization in .NET is not a new thing, but there are two subtle differences that I always forget or get wrong so I figured I’d write them down so I can Google my own answer later.

This is based on .NET 8 and 9. If you come in looking at this later, they may have updated.

Dictionaries

There are Roslyn analyzers that want you to set dictionary-based properties to be get-only.

public class Model
{
  public IDictionary<string, string> WhatAnalyzersWant { get; } = new Dictionary<string, string>()
}
  • Newtonsoft supports this and will add the items to the existing dictionary.
  • System.Text.Json does not support this and the dictionary will remain empty after serialization.

For greatest compatibility between the two frameworks, leave dictionary properties get/set and suppress the analyzer message.

Enums

I work on a lot of services where we specify camelCase style naming, including on the enum members.

To set System.Text.Json up for camelCase enums, it looks like this:

var settings = new JsonSerializerOptions
{
  Converters =
  {
    new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true),
  },
};

For Newtonsoft, it looks like this:

var settings = new JsonSerializerSettings
{
  Converters =
  {
    new StringEnumConverter(new CamelCaseNamingStrategy()),
  },
};

In addition, Newtonsoft supports using System.Runtime.Serialization.EnumMemberAttribute to specify an exact value, which will override the camelCase naming. System.Text.Json does not support this attribute.

public enum Policy
{
  // Only Newtonsoft uses this attribute.
  [EnumMember(Value = "ALWAYS")]
  AlwaysHappens,

  NeverHappens
}

In the above example…

  • Newtonsoft will render ALWAYS and neverHappens.
  • System.Text.Json will render alwaysHappens and neverHappens.

Further, both frameworks allow you to mark an enum with a specific converter, which can also dictate the casing/strategy.

For greatest compatibility between the two frameworks, use the appropriate serializer settings to handle casing on your enum and don’t mark things up with any attributes related to serialization.

personal comments edit

Kai, the most loyal of dog-cats

On Wednesday, April 23, 2025, we had to say our final goodbyes to Kai, who was such a good boy to the very end.

We brought Kai and Stanley home in July 2008 as kittens, brothers from the same litter. Kai was always super territorial, despite being an indoor cat, and would always make sure that cat outside the window knew this was his house.

He had the longest forelegs arms I’ve ever seen on a cat. He loved to stretch out and just barely touch you.

During COVID when we were all home all the time, he became extra dependent on the humans, turning into more of a dog than a cat. He’d patrol around, sit on the floor next to you, and always want to be with the humans.

When no humans were around, he’d balance his time between being in the sun; being in bed; or cuddling with his brother, Stanley. We ran a lot of experiments trying to figure out which was more important - sun, bed, or bro. If he could be in a bed in the sun with his brother, that was the best. I think sun was probably the top ranking thing, though. He really loved being a sunny buddy.

He really wanted to drink your water out of your cup. Didn’t matter how warm it was, it was yours and that made him want it. I 3D printed some drink covers to put on top of your cup if you got up and left. He never figured out you could just flip the top off and get to the cup - it was enough to stop him. He was also a total sweet tooth - if you got some dessert, he really wanted it. We never even gave him any (he couldn’t stomach people food) but something about sweets made him immediately show up.

He never really pooped in a litter box. He’d pee in there, but he just wouldn’t poop in there. That wasn’t too horrible except in the last couple of years it stopped being solid and really was… not good. He also started losing weight, like a lot of weight. He had been a pretty stout boy for a while and by the end of his life he was close to eight pounds, just skin hanging off bones. It was sad to see him change so much.

He turned 17 earlier this month and in the past couple of months his internal health kept deteriorating. On the outside, he’d still run around and play and was active, but inside… he was not doing well. We decided it was probably time to say goodbye.

He loved getting in his carrier, which is so weird because he only ever got taken to the vet in that thing. He wasn’t a traveling cat. But even on the last day, he really wanted in there to go, which just made the trip harder.

We said goodbye to him and we miss him. His brother misses him. Love you, buddy.

docker, linux comments edit

Scenario:

  • You have a Red Hat Enterprise Linux (RHEL) subscription.
  • You are building a series of containers based on RHEL, like:
    • Base container with RHEL 8 and some small amount of packages.
    • In a different repo, a container based on your RHEL 8 base image which adds some packages.
    • Your application container, based on that second image, which adds yet other packages.

The problem: When you build these containers they all seem to be seen by the RHEL subscription-manager as the same logical machine, so you get a bunch of errors like 410 Gone when you try to register the container with the package management system.

My experience is that this is because, by default, the hostname of every container image being built is buildkitsandbox by default. This is set up by the Docker build kit. When you run subscription-manager register to attach to the subscription, depending on a few things (whether you’re using the public customer portal or an internal subscription portal, etc.) you may see registrations get stomped because the same hostname is trying to register from different places at the same time.

The answer: Set the hostname by providing the build argument BUILDKIT_SANDBOX_HOSTNAME to your container.

docker build --build-arg BUILDKIT_SANDBOX_HOSTNAME=something-unique .

Then in your container, just make sure you do some cleanup before installing things.

RUN subscription-manager clean \
  && subscription-manager register --org="XXX" --activationkey="YYY" --force \
  && yum update -y \
  && yum install -y some-package \
  && yum clean all \
  && subscription-manager unregister \
  && subscription-manager clean \
  && rm -rf /var/cache/yum /var/cache/dnf

The combination of the unique hostname and proactive cleanup should get you past the issues.

halloween, costumes comments edit

This year we had 235 trick-or-treaters. That’s a big increase from previous years.

2024: 235 trick-or-treaters.

Average Trick-or-Treaters by Time Block

Year-Over-Year Trick-or-Treaters

Halloween was on a Thursday and it was raining on-and-off. However, there was no school on Friday, so kids were out and prepped for staying up late on a sugar high.

We technically started handing out candy at 5:30 because the number of kids this year starting early was overwhelming. I’ve grouped the 5:30p to 6:00p kids (24) in with the 6:00p - 6:30p kids (22) to total 46 in the 6:00p - 6:30p time block. It throws the curve off a little but we’ll have to live with it.

We didn’t hand out in 2022 (we were remodeling and not home) or 2023 (COVID). It was good to be back in the swing of things. I did throw out my back a few days before, so I tallied the kids while Jenn handed out candy, but we showed up!

Cumulative data:

  Time Block
Year 6:00p - 6:30p 6:30p - 7:00p 7:00p - 7:30p 7:30p - 8:00p 8:00p - 8:30p Total
2006 52 59 35 16 0 162
2007 5 45 39 25 21 139
2008 14 71 82 45 25 237
2009 17 51 72 82 21 243
2010 19 77 76 48 39 259
2011 31 80 53 25 0 189
2013 28 72 113 80 5 298
2014 19 54 51 42 10 176
2015 13 14 30 28 0 85
2016 1 59 67 57 0 184
2019 1 56 59 41 33 190
2021 16 37 30 50 7 140
2024 46 82 52 26 29 235

Our costumes this year:

  • Me: Newt Scamander from Fantastic Beasts and Where to Find Them
  • Jenn: Sally Slater, the tightrope walker from the Disney Haunted Mansion
  • Phoenix: Ursula from The Little Mermaid

I made the coat and the vest for this one. I bought the wand, pants, and shirt. I’m pretty pleased with how it turned out. The coat is really warm and is fully lined - it’s not a costume pattern, it’s a historic reproduction pattern. I wore it to our usual Halloween party but couldn’t wear it all night. It was just too hot.

About halfway through making the coat I tried to push too much fabric through the serger so I broke it. It’s at the shop now. I think I threw off the timing. I also recently acquired a cover stitch machine, but I didn’t see any good places to use it on this costume. I’m excited to try it for a future project.

Me as Newt Scamander (1)

Me as Newt Scamander (2)

azure comments edit

At work I use the az CLI behind a VPN/proxy package called Zscaler. I’m not a big fan of these TLS-intercepting-man-in-the-middle-attack sort of “security” products, but it is what it is.

The problem for me is that, if I move to a new machine, or if someone else is setting up a machine, I always forget how to make the az CLI trust Zscaler so it can function properly and not get a TLS certificate error. I’ve re-figured this out countless times, so this time I’m writing it down. It does seem to be slightly different on Mac and Windows and I’m not sure why. Perhaps it has to do with the different ways the network stack works or something.

The az CLI is Python-based so this will ostensibly work to generally solve Python issues, but I always encounter it as part of az, so I’m blogging it as such.

Zscaler does have some help for enabling trust but you sometimes have to fudge the steps, like with this.

On Mac

  • Make sure you have the Zscaler certificate in your system keychain as a trusted CA. Likely if you have Zscaler running this is already set up.
  • Install the latest ca-certificates package or get the content from here.
  • Set the REQUESTS_CA_BUNDLE environment variable to point at the cert.pem that has all the CA certs in it.

This works because the Homebrew package for ca-certificates automatically includes all the certificates from your system keychain so you don’t have to manually append your custom/company CA info.

On Windows

  • Go get the latest ca-certificates bundle from here.
  • Open that cert.pem file in your favorite text editor. Just make sure you keep the file with LF line endings.
  • Get your Zscaler CA certificate in PEM format. Open that up in the text editor, too.
  • At the bottom of the cert.pem main file, paste in the Zscaler CA certificate contents, thereby adding it to the list of CAs.
  • Set the REQUESTS_CA_BUNDLE environment variable to point at the cert.pem that has all the CA certs in it.

Again, not sure why on Windows you need to have the Zscaler cert added to the main cert bundle but on Mac you don’t. This also could just be something environmental - like there’s something on my work machines that somehow auto-trusts Zscaler but does so to the exclusion of all else.

Regardless, this is what worked for me.